﻿#include "pch.h"
#include "wnd.h"
#include <common/winapi.h>
#include <CommCtrl.h>
#include <atomic>
#include <engine/renderd2d.h>
#include <common/Exception.h>
#include <common/memory.h>
#undef min
#undef max
#pragma comment(lib, "imm32.lib")
#ifdef _WIN32
#pragma comment(lib, "Msimg32.lib")  
#endif // _WIN32

BOOL _wnd_addstyle(HWND hWnd, INT dwStyle, BOOL bExStyle)
{
    auto ret = GetWindowLongPtrW(hWnd, bExStyle ? GWL_EXSTYLE : GWL_STYLE);
    if ((ret & dwStyle) == 0)
    {
        SetWindowLongPtrW(hWnd, bExStyle ? GWL_EXSTYLE : GWL_STYLE, ret | dwStyle);
        return TRUE;
    }
    return FALSE;
}
BOOL _wnd_delstyle(HWND hWnd, INT dwStyle, BOOL bExStyle)
{
    auto ret = GetWindowLongPtrW(hWnd, bExStyle ? GWL_EXSTYLE : GWL_STYLE);
    if ((ret & dwStyle) != 0)
    {
        ret &= ~dwStyle;
        SetWindowLongPtrW(hWnd, bExStyle ? GWL_EXSTYLE : GWL_STYLE, ret);
        return TRUE;
    }
    return FALSE;
}
BOOL _wnd_querystyle(HWND hWnd, INT dwStyle, BOOL bExStyle)
{
    return (GetWindowLongPtrW(hWnd, bExStyle ? GWL_EXSTYLE : GWL_STYLE) & dwStyle) != 0;
}
LRESULT CALLBACK _wnd_shadow_proc(HK_THUNK_DATA* pData, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

    HWND hWnd = pData->hWnd;
    size_t pOld = (size_t)pData->Proc;

    if (uMsg == WM_NCACTIVATE)
    {
        size_t hWndParent = GetWindowLongPtrW(hWnd, GWLP_HWNDPARENT);
        if (!(wParam == 0 && lParam == hWndParent))
        {
            if ((WPARAM)pData->dwData == wParam && lParam == 0)
            {
                SendMessageW((HWND)hWndParent, uMsg, 1, lParam);
                SetFocus((HWND)hWndParent);
                pData->dwData = (LPVOID)1;
                return 0;
            }
            SendMessageW((HWND)hWndParent, uMsg, wParam, lParam);
            pData->dwData = (LPVOID)wParam;
        }
    }
    else if (uMsg == WM_DESTROY)
    {

        SetWindowLongPtrW(hWnd, GWLP_WNDPROC, pOld);
        VirtualFree(pData, 0, MEM_RELEASE);
        return S_FALSE;
    }
    return CallWindowProcW((WNDPROC)pOld, hWnd, uMsg, wParam, lParam);
}


LRESULT CALLBACK _wnd_defwindowprocW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
INT _wnd_wm_nchittest_sizebox(INT width, INT height, INT x, INT y)
{
    INT ret = 0;
    INT nOffset = 5 * 2;
    if (y < nOffset) //鼠标在顶部
    {
        if (x < nOffset) //鼠标在左边
        {
            ret = HTTOPLEFT;
        }
        else if (x > width - nOffset) //鼠标在右边
        {
            ret = HTTOPRIGHT;
        }
        else
        {
            ret = HTTOP;
        }
    }
    else
    {
        if (y > height - nOffset) //鼠标在底部
        {
            if (x < nOffset) //鼠标在左边
            {
                ret = HTBOTTOMLEFT;
            }
            else if (x > width - nOffset) //鼠标在右边
            {
                ret = HTBOTTOMRIGHT;
            }
            else
            {
                ret = HTBOTTOM;
            }
        }
        else
        {
            if (x < nOffset) //鼠标在左边
            {
                if (y < nOffset) //鼠标在上面
                {
                    ret = HTTOPLEFT;
                }
                else
                {
                    if (y > height - nOffset) //鼠标在下面
                    {
                        ret = HTBOTTOMLEFT;
                    }
                    else
                    {
                        ret = HTLEFT;
                    }
                }
            }
            else
            {
                //鼠标在右边
                if (y < nOffset) //鼠标在上面
                {
                    ret = HTTOPRIGHT;
                }
                else
                {
                    if (y > height - nOffset) //鼠标在下面
                    {
                        ret = HTBOTTOMRIGHT;
                    }
                    else
                    {
                        ret = HTRIGHT;
                    }
                }
            }
        }
    }
    return ret;
}
JOUI::UIWnd::UIWnd(INT x, INT y, INT width, INT height, LPCWSTR lpwzWindowName, INT dwStyle, INT dwStyleEx, DWORD dwUIStyle, HWND hWndParent, LPCWSTR lpwzClassName, LPARAM lParam, WinMsgPROC lpfnMsgProc)
{
    CreateUIWnd(x, y, width, height, lpwzWindowName, dwStyle, dwStyleEx, dwUIStyle, hWndParent, lpwzClassName, lParam, lpfnMsgProc);
}
JOUI::UIWnd::UIWnd(HWND hWnd, DWORD dwUIStyle, LPARAM lParam, WinMsgPROC lpfnMsgProc)
{
    wm_bindui(hWnd, dwUIStyle, lParam, lpfnMsgProc);
}
JOUI::UIWnd::~UIWnd()
{
}
BOOL JOUI::UIWnd::CreateUIWnd(INT x, INT y, INT width, INT height, LPCWSTR lpwzWindowName, INT dwStyle, INT dwStyleEx, DWORD dwUIStyle, HWND hWndParent, LPCWSTR lpwzClassName, LPARAM lParam, WinMsgPROC lpfnMsgProc)
{
    LPCWSTR WindowClassName = (lpwzClassName == NULL ? L"Joui.WindowClass.UI" : lpwzClassName);
    if (IsWindow(m_data.hWnd)) return FALSE;

    if (dwStyle == 0)
    {
        dwStyle = WS_OVERLAPPEDWINDOW;
    }
    if (dwStyleEx == 0)
    {
        dwStyleEx = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE;
    }
    dwStyleEx = dwStyleEx | WS_EX_LAYERED;
    HINSTANCE hInst = g_engine_instance;

    if (IsWindow(hWndParent))
        hInst = (HINSTANCE)GetWindowLongPtrW(hWndParent, GWLP_HINSTANCE);

    HWND hWnd = CreateWindowExW(dwStyleEx, WindowClassName, lpwzWindowName, dwStyle, x, y, width, height, hWndParent, NULL, hInst, NULL);
    if (hWnd != 0)
    {
        SendMessageW(hWnd, 128, 0, (LPARAM)g_hIconsm);
        SendMessageW(hWnd, 128, 1, (LPARAM)g_hIcon);

        return wm_bindui(hWnd, dwUIStyle, lParam, lpfnMsgProc);
    }
    return FALSE;
}
void JOUI::UIWnd::Close(BOOL nRet)
{
    if (false == ::IsWindow(m_data.hWnd)) return;
    if (nRet)
        PostMessageW(m_data.hWnd, WM_CLOSE, 0, 0);
    else
        SendMessageW(m_data.hWnd, WM_CLOSE, 0, 0);
}
BOOL JOUI::UIWnd::wm_bindui(HWND hWnd, DWORD dwUIStyle, LPARAM lParam, WinMsgPROC lpfnMsgProc)
{
    if (IsWindow(m_data.hWnd)) return false;
    ShowWindow(hWnd, 0);
    RECT rcWindow{ 0 };
    GetWindowRect(hWnd, &rcWindow);
    SIZE size{};
    size.cx = rcWindow.right - rcWindow.left;
    size.cy = rcWindow.bottom - rcWindow.top;

    if (!FLAGS_CHECK(dwUIStyle, EWS_MESSAGEBOX) && !FLAGS_CHECK(dwUIStyle, EWS_COMBOWINDOW))
    {
        INT offsetX = 0;
        INT offsetY = 0;
        size.cx *= g_drawing_default_dpi;
        size.cy *= g_drawing_default_dpi;
        offsetX = (size.cx - (rcWindow.right - rcWindow.left)) / 2;
        offsetY = (size.cy - (rcWindow.bottom - rcWindow.top)) / 2;
        MoveWindow(hWnd, rcWindow.left - offsetX, rcWindow.top - offsetY, size.cx, size.cy, FALSE);
    }
    m_data.hWnd = hWnd;
    HWND hWndParent = GetWindowOwner(hWnd);
    if (FLAGS_CHECK(dwUIStyle, EWS_MESSAGEBOX))
        hWndParent = ((mbp_s*)lParam)->hWnd;
    else if ((dwUIStyle & wws_centerwindow) == wws_centerwindow)
        CenterFrom();

    GetWindowRect(hWnd, &rcWindow);
    size.cx = rcWindow.right - rcWindow.left;
    size.cy = rcWindow.bottom - rcWindow.top;
    INT dwFlags = 0;
    if (_wnd_querystyle(hWnd, WS_EX_LAYERED, TRUE) || FLAGS_CHECK(dwUIStyle, EWS_MESSAGEBOX))
    {
        dwFlags = EWF_BLAYERED;
    }
    if (hWndParent != 0) 
    {
        m_data.hParent = (UIWnd*)SendMessageW(hWndParent, g_dwMessage, 0, MAKELONG(EMT_DUI, 0));
        if (!FLAGS_CHECK(dwUIStyle, EWS_MESSAGEBOX) && !FLAGS_CHECK(dwUIStyle, EWS_MENU) && !FLAGS_CHECK(dwUIStyle, EWS_COMBOWINDOW))
        {
            EnableWindow(hWndParent, FALSE);
            BringWindowToTop(hWnd);
        }
    }
    auto atomName = GetClassLongPtrW(hWnd, GCW_ATOM);
    if (atomName == ATOM_DIALOG)
        dwFlags = dwFlags | EWF_BMODAL | EWF_ACTIVE;
    m_data.dwUIStyle = dwUIStyle;
    m_data.hImc = ImmGetContext(hWnd);
    m_data.hWndParent = hWndParent;
    m_data.size = { rcWindow.left,rcWindow.top,size.cx ,size.cy };
    m_data.dwFlags = dwFlags | EWF_BLEAVESENT;
    m_data.pfnMsgProc = lpfnMsgProc;
    m_data.padding_shadow = { 12,12,12,12 };
    m_data.size_Border = 1;
    m_data.size_shadow = 10;
    m_data.alpha = 255;
    m_data.size_caption = { 100,40 };
    m_data.margin_caption = { 0,0,0,0 };
    m_data.radius = 0;
    m_UIWindow = this;
    m_UIView = nullptr;
    if (FLAGS_CHECK(dwUIStyle, EWS_MESSAGEBOX))
    {
        m_data.lpMsgParams = (mbp_s*)lParam;
    }
    else
    {
        m_data.lParam = lParam;
    }
    m_data.hPath_radius = new UIPath();
    m_data.crshadow = new UIBrush(UIColor(238, 238, 242, 255));
    m_data.crBorder = new UIBrush(UIColor(245, 245, 245, 255));
    m_data.crbkg = new UIBrush(UIColor(255, 255, 255, 255));
    //sysshadow
    auto style = GetClassLongPtrW(hWnd, GCL_STYLE);
    if ((style & CS_DROPSHADOW) != 0)
    {
        SetClassLongPtrW(hWnd, GCL_STYLE, (style & ~CS_DROPSHADOW));
    }
    INT exStyle = WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE | WS_EX_TOPMOST;
    m_data.hWndShadow = CreateWindowExW(exStyle, L"SysShadow", 0, WS_POPUP, 0, 0, 0, 0, hWnd, 0, 0, 0);
    UIhook::Thunkwindow(m_data.hWndShadow, _wnd_shadow_proc, nullptr);
    //tips
    INT ustyle = TTS_BALLOON; //气泡模式
    m_data.hWndTips = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_TOPMOST, L"tooltips_class32", 0, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_USEVISUALSTYLE, 0, 0, 0, 0, 0, 0, 0, 0);
    SendMessageW(m_data.hWndTips, TTM_SETMAXTIPWIDTH, 0, 2048); //支持多行
    m_data.toolauto = new ti_s();
    m_data.toolauto->cbSize_ = sizeof(ti_s);
    m_data.toolauto->uFlags_ = TTF_SUBCLASS | TTF_TRANSPARENT | TTF_PARSELINKS;
    m_data.toolauto->hWnd_ = hWnd;
    m_data.toolauto->uId_ = m_data.toolauto;
    SendMessageW(m_data.hWndTips, TTM_ADDTOOLW, 0, (LPARAM)m_data.toolauto); 

    m_data.toolrack = new ti_s();
    m_data.toolrack->cbSize_ = sizeof(ti_s);
    m_data.toolrack->uFlags_ = TTF_TRACK | TTF_ABSOLUTE | TTF_TRANSPARENT | TTF_PARSELINKS;
    m_data.toolrack->hWnd_ = hWnd;
    m_data.toolrack->uId_ = m_data.toolrack;
   SendMessageW(m_data.hWndTips, TTM_ADDTOOLW, 0, (LPARAM)m_data.toolrack); 

    m_data.canvas_display = new UICanvas(this, size.cx, size.cy);
    m_data.canvas_bkg = new UICanvas(this, size.cx, size.cy);

    _wnd_addstyle(hWnd, WS_THICKFRAME, FALSE); //窗口具有调整大小边框。 与 WS_SIZEBOX 样式相同。
    UIhook::Thunkwindow(hWnd, WndProc, this);
    _wnd_delstyle(hWnd, WS_THICKFRAME, FALSE);
    ImmAssociateContext(hWnd, FALSE);
    wm_recalcclient(size.cx, size.cy);
    if (!FLAGS_CHECK(dwUIStyle, EWS_MENU))
    {
        if ((dwUIStyle & wws_notitlebar) != wws_notitlebar)
            wm_sys_init();
        else
        {

        }
        if ((dwUIStyle & wws_fullscreen) == wws_fullscreen)
        {
            PostMessageW(m_data.hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
            SetWindowPos(m_data.hWnd, HWND_TOPMOST, 0, 0, 0, 0, 3);
        }
    }

    if (FLAGS_CHECK(dwUIStyle, wws_popupwindow))
    {
        m_data.lpPopupParams = (LPVOID)lParam;
        auto pWnd = (UIWnd*)m_data.hParent;
        if (pWnd)
        {
            HWND old = pWnd->m_data.hWndPopup;
            pWnd->m_data.hWndPopup = hWnd;
            DestroyWindow(old);
            FLAGS_ADD(pWnd->m_data.dwFlags, EWF_BPOPUPWINDOIWSHOWN);
            auto pObj = (UIControl*)lParam;
            if (pObj)
                ImmAssociateContext(hWndParent, pWnd->m_data.hImc);
        }
    }
    
    return TRUE;
}

void JOUI::UIWnd::CenterFrom()
{
    RECT rcWindow{}, rcParent{}, rcArea{};
    GetWindowRect(m_data.hWnd, &rcWindow);
    wnd_getscreenrect(m_data.hWnd, rcParent, rcArea);

    int DlgWidth = rcWindow.right - rcWindow.left;
    int DlgHeight = rcWindow.bottom - rcWindow.top;

    // Find dialog's upper left based on rcCenter
    int xLeft = (rcParent.left + rcParent.right) / 2 - DlgWidth / 2;
    int yTop = (rcParent.top + rcParent.bottom) / 2 - DlgHeight / 2;

    // The dialog is outside the screen, move it inside
    if (xLeft < rcArea.left) xLeft = rcArea.left;
    else if (xLeft + DlgWidth > rcArea.right) xLeft = rcArea.right - DlgWidth;
    if (yTop < rcArea.top) yTop = rcArea.top;
    else if (yTop + DlgHeight > rcArea.bottom) yTop = rcArea.bottom - DlgHeight;
    ::SetWindowPos(m_data.hWnd, NULL, xLeft, yTop, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
    
}

void JOUI::UIWnd::Max()
{
    PostMessageW(m_data.hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
}
void JOUI::UIWnd::Min()
{
    PostMessageW(m_data.hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
}
HWND JOUI::UIWnd::GethWnd()
{
    return m_data.hWnd;
}

HWND JOUI::UIWnd::GethWndParent()
{
    return m_data.hWndParent;
}
LPVOID JOUI::UIWnd::GetUIWnd()
{
    return m_UIWindow;
}
void JOUI::UIWnd::GetClientRect(RECT& lpClientRect)
{
    lpClientRect.right = m_data.size.right;
    lpClientRect.bottom = m_data.size.bottom;
    lpClientRect.right = (FLOAT)lpClientRect.right / g_drawing_default_dpi;
    lpClientRect.bottom = (FLOAT)lpClientRect.bottom / g_drawing_default_dpi;
}

void JOUI::UIWnd::GetRect(RECT& lptRect)
{
    lptRect = m_data.size;
}

LPVOID JOUI::UIWnd::GetFromUIWnd(HWND hWnd)
{
    return (LPVOID)SendMessageW(hWnd, g_dwMessage, 0, MAKELONG(EMT_DUI, 0));
}

LPARAM JOUI::UIWnd::GetlParam()
{
    return m_data.lParam;
}
void JOUI::UIWnd::SetlParam(LPARAM dwlParam)
{
    m_data.lParam = dwlParam;
}
BOOL JOUI::UIWnd::Show(INT nCmdShow)
{
    return_if_false(m_UIWindow, {}, false);
    m_data.dwFlags = m_data.dwFlags | EWF_INTED;
    if (FLAGS_CHECK(m_data.dwUIStyle, wws_popupwindow))
    { //弹出窗不获取焦点
        nCmdShow = SW_SHOWNOACTIVATE;
    }
    if (::GetFocus() == m_data.hWnd)
    {
        FLAGS_ADD(m_data.dwFlags, EWF_ACTIVE);
    }
    ShowWindow(m_data.hWnd, nCmdShow);
    auto atomName = GetClassLongPtrW(m_data.hWnd, GCW_ATOM);
    if (atomName == ATOM_DIALOG) //判断是模态窗口,添加模态窗口风格
    {
        SetFocus(m_data.hWnd);
    }
    InvalidateRect(m_data.hWnd, NULL, FALSE);
    return UpdateWindow(m_data.hWnd);
}
BOOL JOUI::UIWnd::IsVisible()
{
    return IsWindowVisible(m_data.hWnd);
}
void JOUI::UIWnd::SetMinTrackSize(INT MinWidth, INT MinHeight)
{
    m_data.MinTrackSize.x = MinWidth;
    m_data.MinTrackSize.y = MinHeight;
}
BOOL JOUI::UIWnd::SetText(LPCWSTR lpString, BOOL fTleRedraw)
{
    int nError = 0;
    if (((m_data.dwUIStyle & wws_title) == wws_title))
    {
        auto iter = m_hTableObjects.find(std::to_wstring(wws_title));
        if (iter != m_hTableObjects.end())
        {
            auto titles = (UIControl*)iter->second;
            titles->SetText(lpString);
            nError = TRUE;
        }
       
    }
    if (fTleRedraw)
    {
        return nError;
    }
    return SetWindowTextW(m_data.hWnd, lpString);
}
INT JOUI::UIWnd::GetKeys()
{
    INT ret = 0;
    if ((GetAsyncKeyState(VK_CONTROL) & 32768) != 0)
    {
        ret = ret | 1;
    }
    if ((GetAsyncKeyState(VK_SHIFT) & 32768) != 0)
    {
        ret = ret | 2;
    }
    if ((GetAsyncKeyState(VK_LWIN) & 32768) != 0)
    {
        ret = ret | 3;
    }
    if ((GetAsyncKeyState(VK_RWIN) & 32768) != 0)
    {
        ret = ret | 4;
    }
    if ((GetAsyncKeyState(VK_MENU) & 32768) != 0)
    {
        ret = ret | 5;
    }
    if ((GetAsyncKeyState(VK_TAB) & 32768) != 0)
    {
        ret = ret | 6;
    }
    if ((GetAsyncKeyState(VK_RETURN) & 32768) != 0)
    {
        ret = ret | 7;
    }
    if ((GetAsyncKeyState(VK_DELETE) & 32768) != 0)
    {
        ret = ret | 8;
    }
    if ((GetAsyncKeyState(VK_LEFT) & 32768) != 0)
    {
        ret = ret | 9;
    }
    if ((GetAsyncKeyState(VK_UP) & 32768) != 0)
    {
        ret = ret | 10;
    }
    if ((GetAsyncKeyState(VK_RIGHT) & 32768) != 0)
    {
        ret = ret | 11;
    }
    if ((GetAsyncKeyState(VK_DOWN) & 32768) != 0)
    {
        ret = ret | 12;
    }
    return ret;
}
INT JOUI::UIWnd::Flags(INT dwFlags)
{
    if (dwFlags == 0)
        return m_data.dwFlags;
    m_data.dwFlags = dwFlags;
    return 0;
}
LPVOID JOUI::UIWnd::GetToolrack(BOOL iRrack)
{
    if (iRrack)
        return m_data.toolrack;
    else
        return m_data.toolauto;
}
LPCWSTR JOUI::UIWnd::GetText(BOOL fTleGet)
{
    if (fTleGet && m_data.objCaption)
    {
        LPVOID titles = ((UICombutton*)m_data.objCaption)->FindUIView(std::to_wstring(wws_title).c_str());
        if (titles)
        {
            auto objtitles = (UIControl*)titles;
            return StrDupW(objtitles->GetText());
        }
        return NULL;
    }
    else
    {
        int textLen = GetWindowTextLengthW(m_data.hWnd);
        wchar_t* buffer = new wchar_t[textLen + 1];
        GetWindowTextW(m_data.hWnd, buffer, textLen + 1);
        return buffer;
    }
}
LPVOID JOUI::UIWnd::FindUIView(LPCWSTR lpName)
{
    auto iter = m_hTableObjects.find(lpName);
    if (iter != m_hTableObjects.end())
        return iter->second;
    return 0;
}

LRESULT JOUI::UIWnd::WndProc(HK_THUNK_DATA* pData, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HWND hWnd = pData->hWnd;
    WNDPROC pOld = pData->Proc;
    auto window = (UIWnd*)pData->dwData;
    if (window)
    {
        if (window->m_data.pfnMsgProc != nullptr && window->m_data.pfnMsgProc(hWnd, window, window->m_data.objHittest, uMsg, wParam, lParam) == S_FALSE)
        {
            return S_FALSE;
        }
        if (uMsg == WM_DPICHANGED)
        {

        }
        else if (uMsg == g_dwMessage)
        {
            INT16 nType = LOWORD(lParam);
            if (nType == EMT_DUI)
            {
                return (size_t)window;
            }
            else if (nType == EMT_OBJECT)
            {
                auto lpData = (mempoolmsg_s*)wParam;
                if (lpData)
                {
                    ((UIControl*)lpData->pObj)->baseproc(hWnd, lpData->uMsg, lpData->wParam, lpData->lParam);
                    delete lpData;
                }
            }
            return S_OK;
        }
        else if (uMsg == WM_CLOSE)
        {
            if (window->m_data.hWndParent)
            {
                if (GetActiveWindow() != window->m_data.hWndParent)
                {
                    SetActiveWindow(window->m_data.hWndParent);
                }
            }
        }
        else if (uMsg == WM_DESTROY)
        {
            window->m_data.dwFlags |= EWF_BDESTROYWINDOW;

            if (FLAGS_CHECK(window->m_data.dwUIStyle, wws_popupwindow)) {
                auto pWnd = window->m_data.hParent;
                if (pWnd) {
                    FLAGS_DEL(pWnd->m_data.dwFlags, EWF_BPOPUPWINDOIWSHOWN);
                    pWnd->m_data.hWndPopup = 0;
                    auto pObj = (UIControl*)pWnd->m_data.lpPopupParams;
                    if (pObj) {
                        ImmAssociateContext(pWnd->m_data.hWnd, NULL);
                    }
                }
            }

            if ((window->m_data.dwUIStyle & EWS_MESSAGEBOX) == EWS_MESSAGEBOX) {
                if (window->m_data.lpMsgParams != nullptr) {
                    auto CheckBoxObj = (UICheck*)window->m_data.lpMsgParams->lpCheckBoxObj;
                    if (CheckBoxObj) {
                        __set_int(window->m_data.lpMsgParams->lpCheckBoxChecked, 0, CheckBoxObj->GetState());
                    }
                }
            }

            // 销毁子控件
            auto objChildFirst = (UIControl*)window->m_objChildFirst;
            if (objChildFirst) {
                objChildFirst->obj_brothers(WM_DESTROY, 0, 0, FALSE, FALSE);
            }

            // 销毁相关窗口和资源
            if (window->m_data.hWndTips) {
                DestroyWindow(window->m_data.hWndTips);
            }

            DestroyWindow(window->m_data.hWndShadow);

            // 删除相关资源
            delete window->m_data.crBorder;
            delete window->m_data.crshadow;
            delete window->m_data.hPath_Brush;
            delete window->m_data.hrgn_client;
            delete window->m_data.hPath_radius;
            delete window->m_data.canvas_display;
            delete window->m_data.canvas_bkg;

            // 清理父窗口相关操作
            if (window->m_data.hWndParent) {
                EnableWindow(window->m_data.hWndParent, TRUE);
                InvalidateRect(window->m_data.hWndParent, NULL, FALSE);
            }

            // 重置背景图
            window->wm_setbackgImage(0, 0, 0, 0, 0, 0, 0);

            // 清除通知图标
            if (window->m_data.lpNid) {
                Shell_NotifyIconW(NIM_DELETE, (PNOTIFYICONDATAW)window->m_data.lpNid);
                ExMemFree(window->m_data.lpNid);
                window->m_data.lpNid = nullptr;
            }

            // 删除背景颜色资源
            delete window->m_data.crbkg;
            window->m_data.crbkg = nullptr;

            // 清理背景图片资源
            if (window->m_data.lpBackgroundImage) {
                ::KillTimer(window->m_data.hWnd, (UINT_PTR)((size_t)window + TIMER_BKG));
                auto hImg = window->m_data.lpBackgroundImage->hImage;
                if (hImg) {
                    delete hImg;
                }
                delete window->m_data.lpBackgroundImage;
            }

            // 删除工具栏相关资源
            delete window->m_data.toolrack;
            delete window->m_data.toolauto;

            // 取消输入法上下文
            ImmAssociateContext(window->m_data.hWnd, NULL);
            ImmDestroyContext(window->m_data.hImc);

            // 清空对象表
            window->m_hTableObjects.clear();
            window->m_hTableEvent.clear();

            // 清理菜单和焦点对象
            window->m_data.pMenuPrevWnd = nullptr;
            window->m_data.pMenuTrackWnd = nullptr;
            window->m_data.pMenuHostWnd = nullptr;
            window->m_data.objTrack = nullptr;
            window->m_data.objFocus = nullptr;
            window->m_data.objHittest = nullptr;
            window->m_data.objHittestPrev = nullptr;
            window->m_data.objFocusPrev = nullptr;
            window->m_data.objTrackPrev = nullptr;

            // 清理窗口的子控件指针
            window->m_objChildLast = nullptr;
            window->m_objChildFirst = nullptr;

            // 清理消息处理函数
            window->m_data.pfnMsgProc = nullptr;

            // 恢复原窗口过程
            SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (size_t)pOld);

            // 释放动态分配的内存
            VirtualFree(pData, 0, MEM_RELEASE);

        }
        else if (uMsg == WM_NCHITTEST)
        {
            return window->wm_nchittest(lParam);
        }
        else if (uMsg == WM_SETCURSOR) //32
        {
            if (window->wm_setcursor(lParam))
            {
                return S_FALSE;
            }
        }
        else if (uMsg == WM_ERASEBKGND)
        {
            return S_FALSE;
        }
        else if (uMsg == WM_SIZE)
        {
            INT width = GET_X_LPARAM(lParam);
            INT height = GET_Y_LPARAM(lParam);
            window->wm_size(wParam, width, height);
        }
        else if (uMsg == WM_WINDOWPOSCHANGING)
        {
            if (((window->m_data.dwUIStyle & EWS_MENU) == EWS_MENU))
            {
                auto pos = (tagWINDOWPOS*)lParam;
                if (!(pos->flags & SWP_NOSIZE))
                {
                    pos->cy += fScale(4);
                }
                if ((pos->flags & SWP_NOMOVE) != SWP_NOMOVE)
                {
                    if (GetWindow(hWnd, GW_OWNER) != 0)
                    {

                        window->m_data.dwFlags -= (window->m_data.dwFlags & EWF_BMENUREPOSTION);

                        window->wm_menu_setpos(hWnd, pos);
                    }
                    window->m_data.size.left = pos->x;
                    window->m_data.size.top = pos->y;
                    if ((window->m_data.dwFlags & EWF_BMENUINITED) != EWF_BMENUINITED)
                    {
                        window->m_data.dwFlags |= EWF_BMENUINITED;
                        window->wm_menu_createitems(hWnd);
                        ShowWindow(window->m_data.hWndShadow, SW_SHOWNOACTIVATE);

                        SetWindowLongPtrW(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
                        InvalidateRect(hWnd, NULL, FALSE);
                    }
                    return 0;
                }
            }
        }
        else if (uMsg == WM_WINDOWPOSCHANGED) //71
        {
            auto pos = (tagWINDOWPOS*)lParam;
            if (!(pos->flags & SWP_NOMOVE))
            {
                window->m_data.size.left = pos->x;
                window->m_data.size.top = pos->y;
                window->wm_shadow(FALSE, FALSE);
            }
        }
        else if (uMsg == WM_NCCALCSIZE) //131
        {
            return 0;
        }
        else if (uMsg == WM_NCACTIVATE) //134
        {
            window->wm_leavecheck(window->m_data.objHittest, nullptr, lParam, TRUE);
            if (wParam == 0)
            {
                auto objFocus = window->m_data.objFocus;
                window->m_data.dwFlags -= (window->m_data.dwFlags & EWF_ACTIVE);
                window->m_data.objFocusPrev = objFocus;
                if (objFocus)
                    objFocus->KillFocus();
            }
            else
            {
                window->m_data.dwFlags |= EWF_ACTIVE;
                auto objFocus = window->m_data.objFocusPrev;
                if (objFocus)
                    objFocus->SetFocus();
                window->m_data.objFocusPrev = nullptr;
            }
            window->wm_shadow(FALSE, TRUE);
            if (wParam == 0)
            {
                if (lParam == (size_t)(window->m_data.hWndShadow))
                {
                    if (((window->m_data.dwUIStyle & EWS_MESSAGEBOX) == EWS_MESSAGEBOX))
                        return 0;
                }
            }
            return S_FALSE;
        }
        else if (uMsg == WM_NCPAINT)
        {
            return 0;
        }
        else if (uMsg == WM_MOUSEACTIVATE)
        { 
            if (FLAGS_CHECK(window->m_data.dwUIStyle, wws_popupwindow))
            {
                return MA_NOACTIVATE;
            }
        }
        else if (uMsg == WM_NCLBUTTONDOWN)
        {
            window->wm_popupclose();
        }
        else if (uMsg == WM_SHOWWINDOW)
        {
            if (wParam == 0)
            {
                auto objHittest = window->m_data.objHittest;
                if (objHittest)
                {
                    objHittest->DispatchNotify(WM_MOUSELEAVE, 0, 0);
                    window->m_data.objHittest = 0;
                    UpdateWindow(hWnd);
                }
            }
            if (!_wnd_querystyle(hWnd, WS_EX_TOPMOST, TRUE))
            {
                HWND hWndInsertAfter = HWND_BOTTOM;
                if (window->GethWndParent() != 0)
                    hWndInsertAfter = HWND_NOTOPMOST;
                SetWindowPos(window->m_data.hWndShadow, hWndInsertAfter, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOACTIVATE);
            }
            ShowWindow(window->m_data.hWndShadow, (wParam == 0 ? 0 : SW_SHOWNOACTIVATE));
            InvalidateRect(hWnd, NULL, FALSE);
        }
        else if (uMsg == WM_PAINT)
        {
            window->wm_ps();

        }
        else if (uMsg == WM_GETMINMAXINFO)
        {
            window->wm_getmmaxinfo(lParam);
            return 0;
        }
        else if (uMsg == WM_STYLECHANGING)
        {
            if (window->wm_stylechanging(wParam, lParam))
            {
                return 0;
            }
        }
        else if (uMsg == WM_MOUSEWHEEL)
        {
            if ((window->m_data.dwFlags & EWF_BPOPUPWINDOIWSHOWN))
            {
                return 0;
            }
            auto objHittest = (UIScroll*)window->m_data.objHittest;
            if (objHittest)
            {
                if (objHittest->baseproc(hWnd, uMsg, wParam, lParam) == S_OK)
                {
                    if (lstrcmpW(objHittest->m_data.lpClsname, L"form-scroll") == 0)
                    {
                        objHittest->sb_parentnotify((wParam > 0 ? SB_LINEUP : SB_LINEDOWN), 0, 0);
                    }

                    if (((objHittest->m_data.dwStyle & eos_scroll_v) == eos_scroll_v))
                    {
                        objHittest->baseproc(hWnd, WM_VSCROLL, (wParam > 0 ? SB_LINEUP : SB_LINEDOWN), 0);
                    }

                    if (((objHittest->m_data.dwStyle & eos_scroll_h) == eos_scroll_h))
                    {
                        objHittest->baseproc(hWnd, WM_HSCROLL, (wParam > 0 ? SB_LINEUP : SB_LINEDOWN), 0);
                    }
                }
            }
        }
        else if (uMsg >= WM_MOUSEFIRST && uMsg < WM_MOUSELAST) //512 - 526
        {
            window->wm_mouse(uMsg, wParam, lParam);
        }
        else if (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST) //256,264
        {
            window->wm_keyboard(uMsg, wParam, lParam);
        }
        else if (uMsg == WM_ENTERSIZEMOVE) //561
        {
            window->m_data.dwFlags = window->m_data.dwFlags | EWF_BSIZEMOVING;
        }
        else if (uMsg == WM_EXITSIZEMOVE) //562
        {
            window->m_data.dwFlags = window->m_data.dwFlags - (window->m_data.dwFlags & EWF_BSIZEMOVING);
        }
        else if (uMsg == WM_MEASUREITEM) //44
        {
            if (window->wm_measureitem_host(wParam, lParam))
            {
                return 1;
            }
        }
        else if (uMsg == WM_INITDIALOG) //272
        {
            if (((window->m_data.dwUIStyle & EWS_MESSAGEBOX) == EWS_MESSAGEBOX))
            {
                JOUI::UIhook::hook_api_msgbox_initdialog(hWnd, window, wParam, lParam);
                return CallWindowProcW(pOld, hWnd, uMsg, wParam, lParam);
            }
        }
        else if (uMsg == WM_GETDLGCODE) //135
        {
            if (((window->m_data.dwFlags & EWF_BMODAL) == EWF_BMODAL))
            {
                return (DLGC_WANTARROWS | DLGC_WANTTAB | DLGC_WANTALLKEYS | DLGC_WANTMESSAGE | DLGC_HASSETSEL | DLGC_WANTCHARS);
            }
        }
        else if (uMsg == WM_INITMENUPOPUP) //279
        {
            if (((window->m_data.dwUIStyle & EWS_MENU) == EWS_MENU))
            {
                return 0;
            }
            window->wm_initmenupopup((HMENU)wParam);
        }
        else if (uMsg == WM_EXITMENULOOP) //530
        {
            if (((window->m_data.dwUIStyle & EWS_MENU) == EWS_MENU))
            {
                return 0;
            }
            window->m_data.dwFlags = window->m_data.dwFlags - (window->m_data.dwFlags & EWF_BMENUINITED);
            window->wm_nchittest(-1);
            window->wm_leavecheck(window->m_data.objHittest, nullptr, lParam, TRUE);
            window->m_data.dx_counts = 0;
        }
        else if (uMsg == WM_CAPTURECHANGED) //533
        {
            window->wm_obj_untrack(lParam, FALSE);
        }
        else if (uMsg == WM_CONTEXTMENU) //被动接受消息123
        {
            window->wm_menucontext(uMsg, wParam, lParam);
        }
        else if (uMsg == WM_SYSCOMMAND || uMsg == WM_COMMAND) //274,273
        {
            window->wm_command(uMsg, wParam, lParam);
        }
        else if (uMsg == WM_IME_COMPOSITION) //271
        {
            auto objHittest = window->m_data.objHittest;
            if (objHittest)
            {
                if (objHittest->baseproc(hWnd, uMsg, wParam, lParam) == S_OK)
                {
                    window->wm_ime_composition();
                }
            }
         }
        else if (uMsg == WM_SETTEXT) //12
        {
        }
        else if (uMsg == 485) // MN_MSG_SELECTITEM
        {
            if (((window->m_data.dwFlags & EWF_BTRACKOBJECT) == EWF_BTRACKOBJECT))
            {
                POINT pt;
                GetCursorPos(&pt);
                window->wm_mouse(WM_MOUSEMOVE, 1, MAKELONG(pt.x - window->m_data.size.left, pt.y - window->m_data.size.top));
                return 0;
            }
            else
            {
                LONG_PTR item = 0;
                window->wm_menu_mouse(hWnd, WM_MOUSEMOVE, 0, (LONG_PTR*)&wParam);
                if (LODWORD(wParam) == -1)
                {
                    return 0;
                }
                else if(window->m_data.pMenuHostWnd)
                {
                    window->m_data.pMenuHostWnd->m_data.pMenuPrevWnd = window;
                }
            }
        }
        else if (uMsg == 0x1E6) //MN_CANCELMENUS
        {
            auto pMenuHostWnd = window->m_data.pMenuHostWnd;
            if (pMenuHostWnd)
            {
                auto pMenuTrackWnd = (UIWnd*)window->m_data.pMenuTrackWnd;
                if (pMenuTrackWnd)
                {
                    if (((pMenuTrackWnd->m_data.dwFlags & EWF_BTRACKOBJECT) == EWF_BTRACKOBJECT))
                    {
                        pMenuTrackWnd->wm_obj_untrack(lParam, TRUE);
                        return CallWindowProcW(pOld, hWnd, 0x1EF, -1, 0);
                    }
                }
            }
        }
        else if (uMsg == 0x1ED) //MN_BUTTONDOWN
        {
            if (!window->wm_menu_mouse(hWnd, WM_LBUTTONDOWN, 1, (LONG_PTR*)&wParam))
            {
                return CallWindowProcW(pOld, hWnd, uMsg, -1, lParam);
            }

            if (((window->m_data.dwFlags & EWF_BTRACKOBJECT) != EWF_BTRACKOBJECT))
            {
                return CallWindowProcW(pOld, hWnd, uMsg, -1, lParam);
            }
        }
        else if (uMsg == 0x1EF) //MN_BUTTONUP
        {
            window->wm_menu_mouse(hWnd, WM_LBUTTONUP, 0, (LONG_PTR*)&wParam);
            if (window->m_data.objTrackPrev != window->m_data.objHittest)
            {
                return CallWindowProcW(pOld, hWnd, uMsg, -1, lParam);
            }
        }
        else if (uMsg == 0x01F1) //MN_DBLCLK
        {
            return 0;
        }
        else if (uMsg == WM_COPY)
        {
            if (window->wm_keyboard(uMsg, wParam, lParam))
            {
                return S_FALSE;
            }
        }
        else if (uMsg == WM_IME_CHAR)
        {
            if (window->wm_keyboard(uMsg, wParam, lParam))
            {
                return S_FALSE;
            }
        }
    }
    return CallWindowProcW(pOld, hWnd, uMsg, wParam, lParam);
}

BOOL JOUI::UIWnd::RegClass(LPCWSTR lpwzClassName, HICON hIcon, HICON hIconsm)
{
    WNDCLASSEX wcx = { 0 };
    wcx.cbSize = sizeof(WNDCLASSEX);							// size of structure 
    wcx.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;			// redraw if size changes 
    wcx.lpfnWndProc = _wnd_defwindowprocW;					    // points to window procedure 
    wcx.cbClsExtra = 0;											// no extra class memory 
    wcx.cbWndExtra = 0;											// no extra window memory 
    wcx.hInstance = NULL;										// handle to instance 
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);				// predefined app. icon 
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);					// predefined arrow 
    wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	// white background brush 
    wcx.lpszMenuName = NULL;									// name of menu resource 
    wcx.lpszClassName = lpwzClassName;				        	// name of window class 
    wcx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);				// small class icon 

    return RegisterClassExW(&wcx) != 0 || GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
}

INT JOUI::UIWnd::wm_nchittest(LPARAM lParam)
{
    INT dwHitCode = HTCLIENT;
    if (((m_data.dwFlags & EWF_BTRACKOBJECT) == EWF_BTRACKOBJECT))
        return dwHitCode;

    if (!((m_data.dwUIStyle & EWS_MENU) == EWS_MENU))
    {
        if (((m_data.dwFlags & EWF_BMENUINITED) == EWF_BMENUINITED))
        {
            return dwHitCode;
        }
    }
    POINT pt;
    UIControl* objMouse = nullptr;
    if (lParam == -1)
    {
        GetCursorPos(&pt);
    }
    else
    {
        pt.x = GET_X_LPARAM(lParam);
        pt.y = GET_Y_LPARAM(lParam);
    }
    ScreenToClient(m_data.hWnd, &pt);

    if (m_data.hrgn_client && m_data.hrgn_client->HitTest(pt.x, pt.y) == S_OK || m_data.dwWinState == 2)
    {
        wm_nchittestlist((UIControl*)m_objChildLast, pt.x, pt.y, &dwHitCode, objMouse);
        if (objMouse)
        {
            m_data.dwHitCode = dwHitCode;
            if (objMouse == m_data.objCaption)
            {
                dwHitCode = HTCAPTION;
            }
            else if (objMouse->m_data.nID == wws_button_max)
            {
                // dwHitCode = HTMAXBUTTON;
            }
            else
            {
                if ((objMouse->m_data.dwStyle & eos_disabled) == eos_disabled)
                {

                    if ((m_data.dwUIStyle & wws_moveable) == wws_moveable) //允许随意移动
                    {
                        dwHitCode = HTCAPTION;
                    }
                    objMouse = 0;
                }
            }
        }
        else
        {
            if (((m_data.dwUIStyle & wws_moveable) == wws_moveable)) //允许随意移动
            {
                dwHitCode = HTCAPTION;
            }
        }
    }
    else
    {
        //在尺寸边框内

        if (((m_data.dwUIStyle & wws_sizeable) == wws_sizeable)) //允许调整尺寸
        {
            dwHitCode = _wnd_wm_nchittest_sizebox(m_data.size.right, m_data.size.bottom, pt.x, pt.y);
        }
        else
        {
            dwHitCode = HTCAPTION;
        }
    }
    auto objHittest = m_data.objHittest;
    m_data.objHittest = objMouse;
    if (objHittest != objMouse)
    {
        m_data.objHittestPrev = objHittest;
        dwHitCode = HTCLIENT;
    }
    return dwHitCode;
}
void JOUI::UIWnd::wm_shadow(BOOL bUpdateRgn, BOOL bFlush)
{
    if (FLAGS_CHECK(m_data.dwUIStyle, wws_noshadow) && !m_data.crBorder)
        return;

    // 如果窗口已最大化，隐藏阴影窗口
    if (_wnd_querystyle(m_data.hWnd, WS_MAXIMIZE, FALSE))
    {
        ShowWindow(m_data.hWndShadow, SW_HIDE);
        return;
    }
    ShowWindow(m_data.hWndShadow, SW_SHOWNOACTIVATE);

    // 检查是否已经渲染过
    if ((m_data.dwFlags & EWF_BRENDERED) != EWF_BRENDERED)
        return;

    HWND hWnd = m_data.hWndShadow;

    // 计算阴影窗口的位置和大小
    POINT ptDst = { m_data.size.left - m_data.padding_shadow.left, m_data.size.top - m_data.padding_shadow.top };
    SIZE sz = { m_data.size.right + m_data.padding_shadow.left + m_data.padding_shadow.right,
                m_data.size.bottom + m_data.padding_shadow.top + m_data.padding_shadow.bottom };

    MoveWindow(hWnd, ptDst.x, ptDst.y, sz.cx, sz.cy, FALSE);

    // 如果需要更新区域或刷新，执行渲染
    if (bUpdateRgn || bFlush)
    {
        HDC hDC = GetDC(hWnd);
        if (hDC != nullptr)
        {
            // 创建并渲染阴影
            UICanvas cvShadow(this, sz.cx, sz.cy);
            if (cvShadow.BeginDraw() == S_OK)
            {
                // 渲染阴影部分
                if (m_data.crshadow && !FLAGS_CHECK(m_data.dwUIStyle, wws_noshadow))
                {
                    cvShadow.DrawShadow(m_data.crshadow, m_data.padding_shadow.left, m_data.padding_shadow.top,
                        sz.cx - m_data.padding_shadow.right - m_data.size_Border, sz.cy - m_data.padding_shadow.bottom - m_data.size_Border,
                        m_data.size_shadow, m_data.radius, m_data.radius, m_data.radius, m_data.radius);
                }

                // 渲染边框
                if (m_data.crBorder)
                {
                    INT offset = m_data.size_Border;
                    if (FLAGS_CHECK(m_data.dwUIStyle, EWS_MENU))
                        offset = 0;
                    cvShadow.DrawRoundRect(m_data.crBorder, m_data.padding_shadow.left, m_data.padding_shadow.top,
                        sz.cx - m_data.padding_shadow.right - offset, sz.cy - m_data.padding_shadow.bottom - offset,
                        m_data.radius, m_data.size_Border);
                }

                // 获取渲染的 DC
                HDC mDC = nullptr;
                if (cvShadow.GetDC(&mDC) == S_OK)
                {
                    POINT ptSrc = { 0 };
                    BLENDFUNCTION pb_ = { 0 };
                    pb_.BlendOp = 0;
                    pb_.BlendFlags = 0;
                    pb_.SourceConstantAlpha = 0xFF;
                    pb_.AlphaFormat = 1;

                    UpdateLayeredWindow(hWnd, hDC, &ptDst, &sz, mDC, &ptSrc, 0, &pb_, ULW_ALPHA);

                    cvShadow.ReleaseDC();
                }

                cvShadow.EndDraw();
            }

            ReleaseDC(hWnd, hDC);
        }
    }
}

void JOUI::UIWnd::wm_ps()
{
    PAINTSTRUCT ps = { 0 };
    if (((m_data.dwFlags & EWF_INTED) == EWF_INTED))
    {

        BOOL fSized = ((m_data.dwFlags & EWF_SIZED) == EWF_SIZED);
        if (fSized)
        {
            m_data.dwFlags = m_data.dwFlags - (m_data.dwFlags & EWF_SIZED);
        }

        BOOL fLayer = ((m_data.dwFlags & EWF_BLAYERED) == EWF_BLAYERED);

        if (fLayer)
        {
            if (fSized)
            {
                ps.rcPaint.right = m_data.size.right;
                ps.rcPaint.bottom = m_data.size.bottom;
            }
            else
            {
                GetUpdateRect(m_data.hWnd, &ps.rcPaint, FALSE);

            }
        }
        else
        {
            BeginPaint(m_data.hWnd, &ps);
        }
        //限制FPS
        m_data.fpsCounter.LimitFPS();
        wm_render(ps.hdc, ps.rcPaint, fLayer);
        //记录当前FPS
        m_data.fpsCache = m_data.fpsCounter.CalcFPS();
        if (!fLayer)
        {
            EndPaint(m_data.hWnd, &ps);
        }
    }
    if ((m_data.dwFlags & EWF_BRENDERED) != EWF_BRENDERED)
    {
        m_data.dwFlags |= EWF_BRENDERED;
        wm_shadow(TRUE, FALSE);
    }
}
void JOUI::UIWnd::wm_size(WPARAM wParam, INT width, INT height)
{
    m_data.dwFlags = m_data.dwFlags - (m_data.dwFlags & EWF_BRENDERED);
    m_data.dwFlags = m_data.dwFlags | EWF_SIZED | EWF_BREDRAWBACKGROUND;
    if (width != m_data.size.right || height != m_data.size.bottom)
    {

        if (((m_data.dwUIStyle & EWS_MESSAGEBOX) == EWS_MESSAGEBOX))
        {
            _wnd_addstyle(m_data.hWnd, WS_EX_LAYERED, TRUE);
        }
        if (((m_data.dwUIStyle & EWS_MENU) == EWS_MENU))
        {
            width = width - GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
            height = height - GetSystemMetrics(SM_CYFIXEDFRAME) * 2;
        }
    }
    wm_recalcclient(width, height);
    Layout_Update();
    auto objCaption = (UICombutton*)m_data.objCaption;
    if (objCaption)
    {
        RECT rcCaption{ 0 };
        wm_calc_captionrect(rcCaption);
        INT offsetof = (wParam == SIZE_MAXIMIZED) ? 1 : 0;
        objCaption->SetPos(0, 0, rcCaption.right - rcCaption.left + offsetof, rcCaption.bottom - rcCaption.top,
            0, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_EX_NODPISCALE);

        if (m_data.dwWinState != wParam)
        {
            if (((m_data.dwUIStyle & wws_button_max) == wws_button_max))
            {
                auto objmax = (UIControl*)objCaption->FindUIView(std::to_wstring(wws_button_max).c_str());
                if (objmax)
                {
                    objmax->Redraw();
                }
            }

            if (((m_data.dwUIStyle & wws_button_min) == wws_button_min))
            {
                auto objmin = (UIControl*)objCaption->FindUIView(std::to_wstring(wws_button_min).c_str());
                if (objmin)
                {
                    objmin->Redraw();
                }
            }
            m_data.dwWinState = wParam;
        }

        if (wParam == SIZE_MAXIMIZED)//最大化
        {
            m_data.radius = 0;
            if (objCaption)
                objCaption->SetRadius(0, 0, 0, 0);


            if (m_data.hWndParent != 0)
                Move(0, 0);
        }
        else if (wParam == SIZE_RESTORED)
        {
            m_data.radius = m_data.radius_r;
            if (objCaption)
                objCaption->SetRadius(m_data.radius, m_data.radius, 0, 0);

        }
    }
   
    InvalidateRect(m_data.hWnd, 0, FALSE);
}
void JOUI::UIWnd::wm_recalcclient(INT width, INT height)
{
    m_data.size.right = width;
    m_data.size.bottom = height;
    if (m_data.canvas_display->Resize(width, height) == S_OK)
    {
        if (m_data.canvas_bkg->Resize(width, height) == S_OK) //客户区被改变,必定触发背景被改变事件
        {
            m_data.dwFlags = m_data.dwFlags | EWF_BREDRAWBACKGROUND;
        }
    }
    INT nSizebox = 0;
    if (((m_data.dwUIStyle & wws_sizeable) == wws_sizeable))
    {
        nSizebox = 5;
    }
    ExRectF rc = { (float)nSizebox, (float)nSizebox, (float)width - nSizebox, (float)height - nSizebox };
    if (m_data.hrgn_client)
    {
        if (!m_data.hrgn_client->m_size.operator==(rc))
        {
            delete m_data.hrgn_client;
            m_data.hrgn_client = nullptr;
        }
    }
    if(!m_data.hrgn_client)
        m_data.hrgn_client = new UIRegion(rc.left, rc.top, rc.right, rc.bottom);
}
void JOUI::UIWnd::wm_render(LPVOID hDC, RECT rcPaint, BOOL fLayer)
{
    m_data.dwFlags = m_data.dwFlags | EWF_BRENDERING;
    if ((m_data.dwFlags & EWF_BREDRAWBACKGROUND) == EWF_BREDRAWBACKGROUND)
    {
        m_data.dwFlags = m_data.dwFlags - (m_data.dwFlags & EWF_BREDRAWBACKGROUND);
        wm_ps_bkg();
        if (m_data.hPath_Brush)
            delete m_data.hPath_Brush;
        m_data.hPath_Brush = new UIBrush(m_data.canvas_bkg, BrushModeEx::Default, m_data.alpha);
        m_data.hPath_radius->Reset();
        m_data.hPath_radius->BeginPath();
        if (m_data.hPath_radius->StartFigure(0, 0) == S_OK)
        {
            if (m_data.radius)
                m_data.hPath_radius->AddCustomRoundRect(0, 0, m_data.size.right, m_data.size.bottom, m_data.radius, m_data.radius, m_data.radius, m_data.radius);
            else
                m_data.hPath_radius->AddRect(0, 0, m_data.size.right, m_data.size.bottom);
        }
        m_data.hPath_radius->EndPath();

    }
    if (m_data.canvas_display->BeginDraw() == S_OK)
    {
        //处理背景
        ID2D1Bitmap* pBitmapDisplay = (ID2D1Bitmap*)m_data.canvas_display->GetContext(3);
        m_data.canvas_display->Clear();
        m_data.canvas_display->FillPath(m_data.hPath_Brush, m_data.hPath_radius);
        g_d2d_dc->SetTarget(pBitmapDisplay);
        wm_renderPresent((UIControl*)m_objChildFirst, pBitmapDisplay, rcPaint, 0, 0);

        HDC mDC = nullptr;
        if (m_data.canvas_display->GetDC(&mDC) == S_OK)
        {
            POINT ptDest = { m_data.size.left, m_data.size.top };
            POINT point = { 0,0 };
            SIZE pSize = { m_data.size.right, m_data.size.bottom };
            RECT pDirty = { rcPaint.left, rcPaint.top, rcPaint.right, rcPaint.bottom };
            BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
            UPDATELAYEREDWINDOWINFO ulwi_{};
            ulwi_.crKey = 0;
            ulwi_.hdcDst = 0;
            ulwi_.hdcSrc = mDC;
            ulwi_.dwFlags = ULW_ALPHA;
            ulwi_.pblend = &bf;
            ulwi_.pptDst = &ptDest;
            ulwi_.pptSrc = &point;
            ulwi_.prcDirty = &pDirty;
            ulwi_.psize = &pSize;
            ulwi_.cbSize = sizeof(ulwi_);
            _UpdateLayeredWindowIndirect(m_data.hWnd, &ulwi_);
            m_data.canvas_display->ReleaseDC();
        }
        m_data.canvas_display->EndDraw();
    }
    m_data.dwFlags = m_data.dwFlags - (m_data.dwFlags & EWF_BRENDERING);
}
void JOUI::UIWnd::wm_renderPresent(UIControl* objChildFirst, LPVOID pBitmap, RECT rcPaint, INT offsetX, INT offsetY)
{
    ID2D1Bitmap* pBitmapDisplay = (ID2D1Bitmap*)pBitmap;
    UIControl* objNext = objChildFirst;

    while (objNext) {
        auto& data = objNext->m_data;
        RECT rcObj = { 0 };
        rcObj.left = data.Frame.left;
        rcObj.top = data.Frame.top;
        rcObj.right = data.Frame.right;
        rcObj.bottom = data.Frame.bottom;

        // 计算偏移后的矩形
        OffsetRect(&rcObj, offsetX, offsetY);

        // 剪裁矩形与绘制区域的交集
        RECT rcClip = { 0 };
        if (IntersectRect(&rcClip, &rcPaint, &rcObj) && objNext->IsVisible()) {
            // 如果需要重绘且存在可绘制区域
            if ((data.dwFlags & EOF_BCANREDRAW) == EOF_BCANREDRAW) {
                // 如果需要重新绘制
                if ((data.dwFlags & EOF_BNEEDREDRAW) == EOF_BNEEDREDRAW) {
                    data.dwFlags &= ~EOF_BNEEDREDRAW; // 清除重绘标志
                    if (!IsRectEmpty(&data.Frame_d)) {
                        objNext->baseproc(m_data.hWnd, WM_PAINT, 0, 0);
                        g_d2d_dc->SetTarget(pBitmapDisplay);

                        if (data.hPathBrush) {
                            delete data.hPathBrush;
                        }
                        data.hPathBrush = new UIBrush(data.canvas, BrushModeEx::Default, data.alpha);
                    }
                }
            }

            if ((data.dwFlags & EOF_BPATH) == EOF_BPATH && data.hPath_Window) {
                if (data.hPathBrush) {
                    ExMatrix3x2 matrix;
                    matrix.Translate(data.Frame_w.left, data.Frame_w.top);
                    data.hPathBrush->SetTransForm(matrix);
                    m_data.canvas_display->FillPath(data.hPathBrush, data.hPath_Window);
                    matrix.Reset();
                }
            }
            else {
                CanvasDrawMode drawMode = ((data.dwStyleEx & eos_ex_composited) == eos_ex_composited)
                    ? CanvasDrawMode::Over
                    : CanvasDrawMode::Blend;

                m_data.canvas_display->DrawCanvas(data.canvas, rcClip.left, rcClip.top, rcClip.right, rcClip.bottom,
                    rcClip.left - rcObj.left, rcClip.top - rcObj.top, drawMode, data.alpha);
            }

            UIControl* objChild = (UIControl*)objNext->m_objChildFirst;
            if (objChild) {
                wm_renderPresent(objChild, pBitmap, rcClip, rcObj.left, rcObj.top);
            }
        }

        objNext = data.objNext;
    }
}

void JOUI::UIWnd::wm_ps_bkg()
{
    if (m_data.canvas_bkg->BeginDraw() == S_OK)
    {
        UIWnd* p = this;
        if (!m_data.pfnMsgProc || !m_data.pfnMsgProc(m_data.hWnd, p, 0, WM_ERASEBKGND, (size_t)m_data.canvas_bkg, MAKELONG(m_data.size.right, m_data.size.bottom)))
        {
            if ((m_data.dwUIStyle & wws_noinheritbkg) != wws_noinheritbkg)
            {
                auto pParent = m_data.hParent;
                if (pParent)
                {
                    p = pParent;
                }
            }
            if (m_data.fBlur != 0)
            {
                wm_ps_bkg(p);
                m_data.canvas_bkg->blur(nullptr, m_data.fBlur, nullptr, 0, m_data.fmodesoft);
            }
            else
            {
                wm_ps_bkg(p);
            }
        }
        if (((m_data.dwUIStyle & EWS_MESSAGEBOX) == EWS_MESSAGEBOX))
        {
            JOUI::UIhook::hook_api_msgbox_drawinfo(this, m_data.canvas_bkg, m_data.size.right, m_data.size.bottom);
        }
        m_data.canvas_bkg->EndDraw();
    }
}
void JOUI::UIWnd::wm_ps_bkg(UIWnd* p)
{
    //绘制背景
    if (p->m_data.lpBackgroundImage)
    {
        m_data.canvas_bkg->Clear();
        auto bk = p->m_data.lpBackgroundImage;
        BOOL lpGrid = FALSE;
        if (bk->lpGrid)
        {
            if ((bk->lpGrid->left != bk->lpGrid->right) || (bk->lpGrid->top != bk->lpGrid->bottom))
                lpGrid = TRUE;
        }
        if (lpGrid)
        {
            info_GridsImage grids{};
            m_data.canvas_bkg->DrawGridsImage(bk->hImage, bk->lpGrid->left, bk->lpGrid->top, bk->lpGrid->right, bk->lpGrid->bottom, &grids, bk->dwAlpha);
        }
        else if (bk->dwRepeat == bir_default)
        {
            m_data.canvas_bkg->DrawImageRect(bk->hImage, bk->x, bk->y, m_data.size.right, m_data.size.bottom, ImageMode::Default, bk->dwAlpha);
        }
        else if (bk->dwRepeat == bir_epault)
        {
            uint32_t iw = 0, ih = 0;
            auto hImg = bk->hImage;
            if (hImg)
                hImg->GetSize(iw, ih);
            //iw ih=图片原始宽高，ww wh=窗口宽高，sw sh=缩放后宽高，x y=图片绘制坐标
            INT ww = m_data.size.right, wh = m_data.size.bottom, sw = 0, sh = 0, x = bk->x, y = bk->y;

            float sx = static_cast<float>(ww) / iw;
            float sy = static_cast<float>(wh) / ih;
            float sc = std::max(sx, sy);
            sw = iw * sc;
            sh = ih * sc;
            if (sw > ww) x = (ww - sw) / 2;
            else if (sh > wh) y = (wh - sh) / 2;
            m_data.canvas_bkg->DrawImageRect(bk->hImage, x, y, x + sw, y + sh, ImageMode::Default, bk->dwAlpha);
        }
        else if (bk->dwRepeat == bir_epault_center)
        {
            m_data.canvas_bkg->DrawImageRect(bk->hImage, 0, 0, m_data.size.right, m_data.size.bottom, ImageMode::ScaleFill, bk->dwAlpha);
        }
        else if (bk->dwRepeat == bir_no_repeat)
        {
            m_data.canvas_bkg->DrawImage(bk->hImage, bk->x, bk->y, bk->dwAlpha);
        }
        else if (bk->dwRepeat == bir_repeat)
        {
            m_data.canvas_bkg->DrawImageRect(bk->hImage, 0, 0, m_data.size.right, m_data.size.bottom, ImageMode::ScaleCenter, bk->dwAlpha);
        }
        else if (bk->dwRepeat == bir_repeat_center)
        {
            m_data.canvas_bkg->DrawImageRect(bk->hImage, 0, 0, m_data.size.right, m_data.size.bottom, ImageMode::CenterMiddle, bk->dwAlpha);
        }
    }
    else
    {
        //绘制底色
        if (p->m_data.crbkg != 0)
        {
            m_data.canvas_bkg->Clear(p->m_data.crbkg);
        }
    }
}
//限制窗口尺寸
void JOUI::UIWnd::wm_getmmaxinfo(LPARAM lParam)
{
    MINMAXINFO* pMinMaxInfo = reinterpret_cast<MINMAXINFO*>(lParam);
    RECT rcMonitor = { 0 }, rcDesk = { 0 };

    wnd_getscreenrect(m_data.hWnd, rcMonitor, rcDesk);
    INT width = m_data.size.right;
    INT height = m_data.size.bottom;
    if (((m_data.dwUIStyle & wws_fullscreen) == wws_fullscreen)) // 如果是全屏模式
    {
        // 将窗口位置和尺寸设置为整个屏幕的位置和尺寸
        rcDesk = rcMonitor;
        output(rcDesk.right, rcDesk.bottom);
    }
    OffsetRect(&rcDesk, -rcMonitor.left, -rcMonitor.top);
    // 设置左边界和顶边界（不需要减去 1）
    pMinMaxInfo->ptMaxPosition.x = rcDesk.left;
    pMinMaxInfo->ptMaxPosition.y = rcDesk.top;

    // 计算最大宽度和高度
    INT nMaxWidth = rcDesk.right - rcDesk.left;
    INT nMaxHeight = rcDesk.bottom - rcDesk.top;

    // 设置最大宽度和高度
    pMinMaxInfo->ptMaxTrackSize.x = nMaxWidth;
    pMinMaxInfo->ptMaxTrackSize.y = nMaxHeight;

    // 设置允许调整的最小尺寸
    pMinMaxInfo->ptMinTrackSize.x = fScale(m_data.MinTrackSize.x);
    pMinMaxInfo->ptMinTrackSize.y = fScale(m_data.MinTrackSize.y);

    // 设置允许调整的最大尺寸为屏幕尺寸
    pMinMaxInfo->ptMaxSize.x = nMaxWidth;
    pMinMaxInfo->ptMaxSize.y = nMaxHeight;
}
BOOL JOUI::UIWnd::wm_stylechanging(WPARAM wParam, LPARAM lParam)
{
    STYLESTRUCT* pStyleStruct = reinterpret_cast<STYLESTRUCT*>(lParam);
    BOOL ret = FALSE;
    INT styleNew = pStyleStruct->styleNew;
    INT styleDui = m_data.dwUIStyle;

    if (wParam == GWL_EXSTYLE)
    {

        if (((styleDui & EWS_MESSAGEBOX) == EWS_MESSAGEBOX))
        {
            m_data.dwFlags = m_data.dwFlags | EWF_BLAYERED;
        }
        else
        {

            if ((pStyleStruct->styleNew & WS_EX_LAYERED))
            {
                m_data.dwFlags = m_data.dwFlags - (m_data.dwFlags & EWF_BLAYERED);
            }
            else
            {

                if (((m_data.dwFlags & EWF_BLAYERED) == EWF_BLAYERED))
                {
                    m_data.dwFlags = m_data.dwFlags - (m_data.dwFlags & EWF_BLAYERED);
                }
            }
        }
        InvalidateRect(m_data.hWnd, NULL, FALSE);
    }
    else
    {
        styleNew = (styleNew & ~(WS_DLGFRAME | WS_BORDER));
        if ((styleDui & wws_button_min) == 0 || (styleDui & wws_fullscreen) == wws_fullscreen)
        {
            styleNew = (styleNew & ~WS_MINIMIZEBOX);
        }
        else
        {
            styleNew = styleNew | WS_MINIMIZEBOX;
        }

        if ((styleDui & wws_button_max) == 0 || (styleDui & wws_fullscreen) == wws_fullscreen)
        {
            styleNew = (styleNew & ~WS_MAXIMIZEBOX);
        }
        else
        {
            styleNew = styleNew | WS_MAXIMIZEBOX;
        }

        if ((styleDui & wws_fullscreen) == wws_fullscreen)
        {
            styleNew = (styleNew & ~WS_SYSMENU);
        }
        else
        {
            styleNew = styleNew | WS_SYSMENU;
        }

        if ((styleDui & wws_sizeable) == wws_sizeable)
        {
            styleNew = styleNew | WS_THICKFRAME;
        }
        else
        {
            styleNew = styleNew & ~WS_THICKFRAME;
        }

        if (styleNew != pStyleStruct->styleNew)
        {
            pStyleStruct->styleNew = styleNew;
            ret = TRUE;
        }
    }
    return ret;
}

void JOUI::UIWnd::wm_nchittestlist(UIControl* objLast, INT x, INT y, INT* hitCode, UIControl*& objMouse)
{
    UIControl* objPrev = objLast;
    while (objPrev)
    {
        if (((objPrev->m_data.dwStyle & eos_hidden) != eos_hidden))
        {
            POINT po = { x, y };
            if (PtInRect(&objPrev->m_data.Frame, po))
            {
                INT ox = x - objPrev->m_data.Frame.left;
                INT oy = y - objPrev->m_data.Frame.top;
                auto lParam = MAKELONG(ox, oy);
                if (((objPrev->m_data.dwStyle & eos_disabled) == eos_disabled)) //检测是否被禁止，被禁止组件还需检测是否可穿透
                {
                    *hitCode = objPrev->baseproc(m_data.hWnd, WM_NCHITTEST, 0, lParam);
                    if (*hitCode == HTTRANSPARENT) //在穿透区域,则继续寻找
                    {
                        objPrev = objPrev->m_data.objPrev;
                        continue;
                    }
                    else
                    {
                        *hitCode = HTCLIENT; //不可穿透则直接停止寻找
                        objMouse = objPrev;
                        return;
                    }
                }
                else
                {
                    objLast = (UIControl*)objPrev->m_objChildLast;
                    if (objLast != 0)
                    {
                        UIControl* tmp = nullptr;
                        wm_nchittestlist(objLast, ox, oy, hitCode, tmp);
                        if (tmp && *hitCode != HTTRANSPARENT) //找到目标子组件
                        {
                            objMouse = tmp;
                            return;
                        }
                    }
                }
                *hitCode = objPrev->baseproc(m_data.hWnd, WM_NCHITTEST, 0, lParam);
                if (*hitCode == HTTRANSPARENT) // 如果可穿透，则继续寻找
                {
                    objPrev = objPrev->m_data.objPrev;
                    continue;
                }
                else
                {
                    m_data.dwHitObjPos_Abs = lParam;
                    objMouse = objPrev;
                    return;
                }
            }
        }
        objPrev = objPrev->m_data.objPrev;
    }
}

BOOL JOUI::UIWnd::wm_setcursor(LPARAM lParam)
{
    auto uHitCode = LOWORD(lParam);
    static const std::unordered_map<WORD, LPCWSTR> cursorMap = {
        {HTCAPTION, IDC_ARROW},
        {HTTOPLEFT, IDC_SIZENWSE},
        {HTTOPRIGHT, IDC_SIZENESW},
        {HTTOP, IDC_SIZENS},
        {HTBOTTOMLEFT, IDC_SIZENESW},
        {HTBOTTOMRIGHT, IDC_SIZENWSE},
        {HTBOTTOM, IDC_SIZENS},
        {HTLEFT, IDC_SIZEWE},
        {HTRIGHT, IDC_SIZEWE}
    };

    // 如果是 HTCLIENT，尝试使用 objHittest 处理
    if (uHitCode == HTCLIENT)
    {
        auto objHittest = m_data.objHittest;
        if (objHittest && objHittest->baseproc(m_data.hWnd, WM_SETCURSOR, m_data.dwHitObjPos_Abs, MAKELONG(m_data.dwHitCode, HIWORD(lParam))) != S_OK)
        {
            return TRUE; // 如果 baseproc 处理了光标设置，返回 TRUE
        }
    }
    else
    {
        auto it = cursorMap.find(uHitCode);
        if (it != cursorMap.end())
        {
            SetCursor(LoadCursorW(nullptr, it->second));
            return TRUE;
        }
    }
    return FALSE;
}


INT JOUI::UIWnd::wm_popupclose()
{
    INT result = 0;
    if (FLAGS_CHECK(m_data.dwFlags, EWF_BPOPUPWINDOIWSHOWN))
    {
        if (m_data.lpPopupParams == 0)
        {
            FLAGS_DEL(m_data.dwFlags, EWF_BPOPUPWINDOIWSHOWN);
            result = DestroyWindow(m_data.hWndPopup);
        }
    }
    return result;
}

void JOUI::UIWnd::wm_leavecheck(UIControl* objCheck, UIControl* objHittest, LPARAM lParam, BOOL fTrack)
{
    if (!objHittest)
    {
        objHittest = m_data.objHittest;  // 默认使用 m_data.objHittest
    }

    if (objCheck == objHittest)
    {
        return;
    }

    // 如果 objCheck 存在且没有触发 WM_MOUSELEAVE
    if (objCheck && (m_data.dwFlags & EWF_BLEAVESENT) != EWF_BLEAVESENT)
    {
        m_data.dwFlags |= EWF_BLEAVESENT;
        objCheck->baseproc(m_data.hWnd, WM_MOUSELEAVE, 0, lParam);  // 触发 WM_MOUSELEAVE
    }

    // 如果 objHittest 存在，触发 WM_MOUSEHOVER
    if (objHittest)
    {
        m_data.dwFlags &= ~EWF_BLEAVESENT;  // 清除 EWF_BLEAVESENT 标志
        m_data.objHittestPrev = objHittest;
        objHittest->baseproc(m_data.hWnd, WM_MOUSEHOVER, 0, lParam);  // 触发 WM_MOUSEHOVER
    }

    // 如果启用了 fTrack，设置鼠标跟踪定时器
    if (objHittest && fTrack)
    {
        size_t nEvent = (size_t)this + TIMER_MOUSETRACK;
        ::KillTimer(m_data.hWnd, nEvent); 
        ::SetTimer(m_data.hWnd, nEvent, 50, wm_mousetrack_timer); 
    }
}


void JOUI::UIWnd::wm_mousetrack_timer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
    ::KillTimer(hWnd, idEvent);
    UIWnd* pWnd = (UIWnd*)(idEvent - TIMER_MOUSETRACK);
    if ((pWnd->m_data.dwFlags & EWF_BTRACKOBJECT) != EWF_BTRACKOBJECT)
    {
        POINT pt;
        GetCursorPos(&pt);
        if (WindowFromPoint(pt) != hWnd)
        {
            if ((pWnd->m_data.dwFlags & EWF_BLEAVESENT) != EWF_BLEAVESENT)
            {
                pWnd->m_data.dwFlags |= EWF_BLEAVESENT;
                auto objHit = pWnd->m_data.objHittestPrev;
                if (objHit)
                {
                    objHit->baseproc(hWnd, WM_MOUSELEAVE, 0, 0); //发送离开
                    pWnd->m_data.objHittestPrev = pWnd->m_data.objHittest;
                    pWnd->m_data.objHittest = nullptr;
                }
               
            }
        }
    }
}

void JOUI::UIWnd::wm_backimage_timer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
    auto pObj = (UIWnd*)(idEvent - TIMER_BKG);
    auto lpBI = pObj->m_data.lpBackgroundImage;
    if (lpBI != 0)
    {
        INT iCur = lpBI->curFrame + 1;
        INT iMax = lpBI->maxFrame - 1;
        if (iCur > iMax)
        {
            iCur = 0;
        }
        auto img = lpBI->hImage;
        img->SetCurFrame(iCur);
        lpBI->curFrame = iCur;

        pObj->Update();
        UpdateWindow(hWnd);
    }
}

void JOUI::UIWnd::wm_ime_composition()
{
    auto hImc = ImmGetContext(m_data.hWnd);
    if (hImc != 0)
    {
        POINT pt;
        GetCaretPos(&pt);
        if (FLAGS_CHECK(m_data.dwFlags, EWF_BPOPUPWINDOIWSHOWN))
        {
            RECT rect;
            GetWindowRect(m_data.hWndPopup, &rect);
            pt.x += rect.left - m_data.size.left;
            pt.y += rect.top - m_data.size.top;
        }
        COMPOSITIONFORM cf;
        cf.dwStyle = CFS_POINT;
        cf.ptCurrentPos.x = pt.x;
        cf.ptCurrentPos.y = pt.y;
        ImmSetCompositionWindow(hImc, &cf);
        auto objFocus = m_data.objFocus;
        if (objFocus)
        {
            UIFont *hFont = objFocus->m_data.hFont;
            if (hFont == 0)
                hFont = g_default_font;
            LOGFONTW Logfont{ 0 };
            hFont->GetLogFont(&Logfont);
            ImmSetCompositionFontW(hImc, &Logfont);
        }
        ImmReleaseContext(m_data.hWnd, hImc);
    }
}

BOOL JOUI::UIWnd::wm_keyboard(INT uMsg, WPARAM wParam, LPARAM lParam)
{
    auto objFocus = m_data.objFocus;
    BOOL bPopupWindowShown = FALSE;
    
    // 处理弹出窗口
    if (objFocus && uMsg != WM_IME_CHAR)
    {
        if (FLAGS_CHECK(m_data.dwFlags, EWF_BPOPUPWINDOIWSHOWN))
        {
            if (uMsg >= WM_KEYDOWN && uMsg <= WM_KEYLAST)
            {
                SendMessageW(m_data.hWndPopup, uMsg, wParam, lParam);
                bPopupWindowShown = TRUE;
            }
        }
        if (objFocus->baseproc(m_data.hWnd, uMsg, wParam, lParam) == S_FALSE)
        {
            return TRUE;
        }
    }
    switch (uMsg)
    {
    case WM_KEYDOWN:
        return wm_handlekeydown(wParam, objFocus, bPopupWindowShown, lParam);

    case WM_KEYUP:
        return wm_handlekeyup(wParam, objFocus);

    case WM_COPY:
        return wm_handlecopy(uMsg, objFocus);

    case WM_IME_CHAR:
        return wm_handleimechar(wParam, lParam, objFocus);

    default:
        return 0;
    }
}

BOOL JOUI::UIWnd::wm_handlekeydown(WPARAM wParam, UIControl* objFocus, BOOL bPopupWindowShown, LPARAM lParam)
{
    if (wParam == VK_ESCAPE)
    {
        if ((m_data.dwUIStyle & wws_escexit) == wws_escexit && !bPopupWindowShown)
        {
            SendMessageW(m_data.hWnd, WM_CLOSE, 0, 0);
        }
    }
    else if (wParam == VK_SPACE)
    {
        if (GetKeys() == 0 && objFocus && ((objFocus->m_data.dwFlags & EOF_BDISABLESPACEANDENTER) != EOF_BDISABLESPACEANDENTER))
        {
            wm_buttondown(objFocus, WM_LBUTTONDOWN, 0, 0);
        }
    }
    else if (wParam == VK_TAB)
    {
        wm_obj_settabstop(objFocus);
    }
    else if (wParam == VK_UP || wParam == VK_DOWN || wParam == VK_LEFT || wParam == VK_RIGHT)
    {
        if ((m_data.dwUIStyle & EWS_MENU) == EWS_MENU)
        {
            return 1; // 禁止菜单项的方向键操作
        }
    }

    if ((m_data.dwFlags & EWF_BMODAL) == EWF_BMODAL)
    {
        UINT retvalue = MapVirtualKeyW(wParam, 2); // 判断是否为输入法字符
        if (retvalue && objFocus)
        {
            BYTE keyboardState[256] = { 0 };
            GetKeyboardState(keyboardState);
            WCHAR charactersPressed[2] = {};
            ToUnicode(wParam, lParam, keyboardState, charactersPressed, 2, 0);
            objFocus->baseproc(m_data.hWnd, WM_CHAR, (WPARAM)charactersPressed[0], lParam);
        }
    }

    return 0;
}

BOOL JOUI::UIWnd::wm_handlekeyup(WPARAM wParam, UIControl* objFocus)
{
    if (wParam == VK_SPACE && objFocus)
    {
        if ((objFocus->m_data.dwFlags & EOF_BDISABLESPACEANDENTER) != EOF_BDISABLESPACEANDENTER)
        {
            wm_mouse(WM_LBUTTONUP, 0, 0);
            objFocus->baseproc(m_data.hWnd, WM_EX_LCLICK, 0, 0);
        }
    }
    return 0;
}

BOOL JOUI::UIWnd::wm_handlecopy(UINT uMsg, UIControl* objFocus)
{
    if ((m_data.dwUIStyle & EWS_MESSAGEBOX) == EWS_MESSAGEBOX && objFocus)
    {
        if (lstrcmpW(objFocus->m_data.lpClsname, L"form-edit") == 0)
        {
            return 1; // 阻止消息框中的复制操作
        }
    }
    return 0;
}

BOOL JOUI::UIWnd::wm_handleimechar(WPARAM wParam, LPARAM lParam, UIControl* objFocus)
{
    if ((m_data.dwFlags & EWF_BMODAL) == EWF_BMODAL && objFocus)
    {
        objFocus->baseproc(m_data.hWnd, WM_CHAR, wParam, lParam);
    }
    return 0;
}


void JOUI::UIWnd::wm_mouse(INT uMsg, WPARAM wParam, LPARAM lParam)
{
    UIControl* parent = nullptr;
    if (((m_data.dwFlags & EWF_BTRACKOBJECT) == EWF_BTRACKOBJECT)) //是否按住组件
    {
        parent = m_data.objTrack;
    }
    else
    {
        parent = m_data.objHittest;
    }
    INT dwHitCode = m_data.dwHitCode;
    if (parent)
    {
        auto pObjLast = parent;
        if (((m_data.dwFlags & EWF_BTRACKOBJECT) == EWF_BTRACKOBJECT))
        {
            lParam = MAKELONG(GET_X_LPARAM(lParam) - pObjLast->m_data.Frame_w.left, GET_Y_LPARAM(lParam) - pObjLast->m_data.Frame_w.top);
            m_data.dwHitObjPos_Abs = lParam;
        }
        else
        {
            lParam = m_data.dwHitObjPos_Abs;
        }
    }
    if (uMsg == WM_MOUSEMOVE)
    {
        if (wParam == 0)
        {
            wm_leavecheck(m_data.objHittestPrev, parent, lParam, TRUE);
        }
        if (parent)
        {
            parent->baseproc(m_data.hWnd, uMsg, wParam, lParam);
        }
    }
    else if (parent)
    {
        if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || uMsg == WM_MBUTTONDOWN)
        {
            if (uMsg == WM_LBUTTONDOWN)
            {
                wm_popupclose();
            }
            wm_buttondown(parent, uMsg, wParam, lParam);
        }
        else if (uMsg == WM_LBUTTONUP || uMsg == WM_RBUTTONUP || uMsg == WM_MBUTTONUP)
        {
            wm_obj_untrack(lParam, FALSE);
            parent->baseproc(m_data.hWnd, uMsg, wParam, lParam);
        }
        else if (uMsg == WM_LBUTTONDBLCLK || uMsg == WM_RBUTTONDBLCLK || uMsg == WM_MBUTTONDBLCLK)
        {
            INT newMsg = uMsg;
            if (uMsg == WM_LBUTTONDBLCLK)
            {
                newMsg = WM_LBUTTONDBLCLK;
            }
            else if (uMsg == WM_RBUTTONDBLCLK)
            {
                newMsg = WM_RBUTTONDOWN;
            }
            else
            {
                newMsg = WM_MBUTTONDOWN;
            }
            wm_buttondown(parent, newMsg, wParam, lParam);

        }
    }
}

void JOUI::UIWnd::wm_obj_settabstop(UIControl* objLastFocus)
{
    auto pObjLast = objLastFocus;
    if (pObjLast)
    {
        BOOL objNextFocus = wm_obj_setnextfocus(pObjLast->m_data.objNext, objLastFocus);
        if (objNextFocus == FALSE)
        {
            auto objParent = (UIControl*)pObjLast->m_data.Parent->m_UIView;
            if (objParent)
            {
                wm_obj_setnextfocus((UIControl*)objParent->m_objChildFirst, objLastFocus);
            }
            else
            {
                wm_obj_setnextfocus((UIControl*)m_objChildFirst, objLastFocus);
            }
        }
    }
    else
    {
        wm_obj_setnextfocus((UIControl*)m_objChildFirst, objLastFocus);
    }
}

BOOL JOUI::UIWnd::wm_obj_setnextfocus(UIControl* objEntry, UIControl* objLastFocus)
{
    auto pObjEntry = objEntry;
    while (pObjEntry)
    {

        if (((pObjEntry->m_data.dwStyleEx & eos_ex_tabstop) == eos_ex_tabstop))
        {
            if (objLastFocus != objEntry)
            {
                return pObjEntry->SetFocus();
            }
        }
        pObjEntry = pObjEntry->m_data.objNext;
    }
    return FALSE;
}

void JOUI::UIWnd::wm_buttondown(UIControl* parent, INT uMsg, WPARAM wParam, LPARAM lParam)
{
    if ((m_data.dwFlags & EWF_BTRACKOBJECT) != EWF_BTRACKOBJECT)
    {
        m_data.dwFlags = m_data.dwFlags - (m_data.dwFlags & (EWF_BLEFTTRACK | EWF_BRIGHTTRACK | EWF_BMIDTRACK | EWF_BLEFTDTRACK));
        m_data.dwFlags |=  EWF_BTRACKOBJECT;
        if (uMsg == WM_LBUTTONDOWN)
        {
            m_data.dwFlags |= EWF_BLEFTTRACK;
            if (parent)
                parent->SetFocus();
        }
        else if (uMsg == WM_LBUTTONDBLCLK)
        {
            m_data.dwFlags |=  EWF_BLEFTDTRACK;
            if (parent)
                parent->SetFocus();
        }
        else if (uMsg == WM_RBUTTONDOWN)
        {
            m_data.dwFlags |=  EWF_BRIGHTTRACK;
        }
        else
        {
            m_data.dwFlags |= EWF_BMIDTRACK;
        }
        m_data.objTrack = parent;
        if (parent)
        {
            parent->TooltipsPop(0);
            parent->baseproc(m_data.hWnd, uMsg, wParam, lParam);
        }
        if (wParam != 0)
        {
            SetCapture(m_data.hWnd); 
        }
    }
}

void JOUI::UIWnd::wm_obj_untrack(LPARAM lParam, BOOL fMsgDispatch)
{
    if (((m_data.dwFlags & EWF_BTRACKOBJECT) == EWF_BTRACKOBJECT))
    {
        m_data.dwFlags = m_data.dwFlags - (m_data.dwFlags & EWF_BTRACKOBJECT);
        auto objTrack = m_data.objTrack;
        m_data.objTrack = 0;
        m_data.objTrackPrev = objTrack;
        if (!fMsgDispatch)
        {
            ReleaseCapture(); 
        }
        if (objTrack)
        {
            POINT pt;
            GetCursorPos(&pt);
            INT uMsg = 0;
            if (fMsgDispatch)
            {

                if ((m_data.dwFlags & EWF_BLEFTTRACK) == EWF_BLEFTTRACK)
                {
                    uMsg = WM_LBUTTONUP;
                }
                else if ((m_data.dwFlags & EWF_BRIGHTTRACK) == EWF_BRIGHTTRACK)
                {
                    uMsg = WM_RBUTTONUP;
                }
                else if ((m_data.dwFlags & EWF_BMIDTRACK) == EWF_BMIDTRACK)
                {
                    uMsg = WM_MBUTTONUP;
                }
                objTrack->baseproc(m_data.hWnd, uMsg, 0, 0);

                wm_nchittest(MAKELONG(pt.x, pt.y));
                wm_leavecheck(objTrack, nullptr, lParam, FALSE);
            }
            else
            {
                wm_nchittest(MAKELONG(pt.x, pt.y));
                wm_leavecheck(objTrack, nullptr, lParam, FALSE);
                if (m_data.objHittest == objTrack)
                {
                    if (((m_data.dwFlags & EWF_BLEFTTRACK) == EWF_BLEFTTRACK))
                    {
                        uMsg = WM_EX_LCLICK;
                    }
                    else if (((m_data.dwFlags & EWF_BLEFTDTRACK) == EWF_BLEFTDTRACK))
                    {
                        uMsg = WM_EX_LDCLICK;
                    }
                    else if (((m_data.dwFlags & EWF_BRIGHTTRACK) == EWF_BRIGHTTRACK))
                    {
                        uMsg = WM_EX_RCLICK;
                    }
                    else
                    {
                        uMsg = WM_EX_MCLICK;
                    }
                    objTrack->baseproc(m_data.hWnd, uMsg, 0, m_data.dwHitObjPos_Abs);
                }
            }
        }
    }
}

void JOUI::UIWnd::wm_menucontext(INT uMsg, WPARAM wParam, LPARAM lParam)
{
    auto objHittest = m_data.objHittest;
    if (objHittest)
    {
        m_data.objMenucontext = objHittest;
        m_data.dwFlags |= EWF_BMENUCONTEXT;
        objHittest->baseproc(m_data.hWnd, uMsg, wParam, lParam);
        m_data.dwFlags -= (m_data.dwFlags & EWF_BMENUCONTEXT);
    }
}

void JOUI::UIWnd::wm_command(INT uMsg, WPARAM wParam, LPARAM lParam)
{
    auto objMenucontext = m_data.objMenucontext;
    m_data.objMenucontext = 0;
    if (objMenucontext)
    {
        objMenucontext->baseproc(m_data.hWnd, uMsg, wParam, lParam);
    }
    if (((m_data.dwFlags & EWF_BMODAL) == EWF_BMODAL))
    {
        if (wParam == 2)
        {
            EndDialog(m_data.hWnd, IDCANCEL);
        }

    }
}

BOOL JOUI::UIWnd::wm_setbackgImage(UIImage *hImg, INT x, INT y, DWORD dwRepeat, RECT* lpGrid, INT dwFlags, DWORD dwAlpha)
{
    if (m_data.lpBackgroundImage)
    {
        ::KillTimer(m_data.hWnd, (UINT_PTR)((size_t)this + TIMER_BKG));
        auto hImage = m_data.lpBackgroundImage->hImage;
        delete hImage;
        delete m_data.lpBackgroundImage;
        m_data.lpBackgroundImage = nullptr;
    }
    if (hImg)
    {
        UIImage* hImage = nullptr;
        INT width = 0, height = 0;
        if (dwFlags != bif_disablescale && dwFlags != bif_playimage)
        {
            hImg->Scale(m_data.size.right, m_data.size.bottom, &hImage);
            delete hImg;
        }
        else
        {
            hImage = hImg;
        }

        if (hImage != 0)
        {
            m_data.lpBackgroundImage = new info_backgroundimage();
            if (m_data.lpBackgroundImage)
            {
                m_data.lpBackgroundImage->dwFlags = dwFlags;
                m_data.lpBackgroundImage->hImage = hImage;
                m_data.lpBackgroundImage->x = x;
                m_data.lpBackgroundImage->y = y;
                m_data.lpBackgroundImage->dwRepeat = dwRepeat;
                m_data.lpBackgroundImage->dwAlpha = dwAlpha;
                m_data.lpBackgroundImage->lpGrid = lpGrid;

                if (hImage->GetFrameCount() > 1)
                {
                    INT nDelay = 0;
                    if (hImage->GetFrameDelay(nDelay, 1))
                    {
                        m_data.lpBackgroundImage->nDelay = nDelay;
                        m_data.lpBackgroundImage->maxFrame = hImage->GetFrameCount();
                        if ((dwFlags & bif_playimage) != 0)
                        {
                            ::SetTimer(m_data.hWnd, ((size_t)this + TIMER_BKG), nDelay * 10, wm_backimage_timer);
                        }
                    }
                }
                else
                {
                    Update();
                }
                return TRUE;
            }
        }
    }
    return FALSE;
}

void JOUI::UIWnd::wm_calc_captionrect(RECT& rcCaption)
{
    rcCaption.right = m_data.size_caption.cx / 100 * m_data.size.right; //保证百分比
    rcCaption.bottom = m_data.size_caption.cy * g_drawing_default_dpi;     //绝对高度

    rcCaption.left = m_data.margin_caption.left * g_drawing_default_dpi;
    rcCaption.top = m_data.margin_caption.top * g_drawing_default_dpi;
    rcCaption.right = rcCaption.right - (m_data.margin_caption.right * g_drawing_default_dpi);
    rcCaption.bottom = rcCaption.bottom + rcCaption.top;
}

void JOUI::UIWnd::wm_sys_init()
{
    RECT rcCaption = { 0 };
    wm_calc_captionrect(rcCaption);
    INT nMinHeight = 0;
    BOOL uMsgStyle = FALSE;
    LPCWSTR lpTitle = nullptr;
    //FLAGS_CHECK(dwUIStyle, wws_notitlebar)
    INT ShowStyle = (FLAGS_CHECK(m_data.dwUIStyle, wws_notitlebar) ? eos_hidden : 0);
    if ((m_data.dwUIStyle & EWS_MESSAGEBOX) == EWS_MESSAGEBOX)
    {
        uMsgStyle = TRUE;
        lpTitle = m_data.lpMsgParams->lpTitle;
    }
    else
    {

        lpTitle = GetText();
    }
    auto objCaption = new UICombutton(this, 0, 0, rcCaption.right - rcCaption.left, rcCaption.bottom - rcCaption.top, lpTitle, wws_title | ShowStyle,
        ((m_data.dwUIStyle & wws_nocaptiontopmost) != 0 ? 0 : eos_ex_topmost | eos_ex_focusable), wws_title, Left | Middle | EndEllipsis);

    //pObjCaption->outXml = TRUE;
    nMinHeight = rcCaption.bottom + rcCaption.top;
    INT nMinWidth = objCaption->Reset();

    RECT tmp{};
    objCaption->GetRect(tmp, grt_textoff);
    objCaption->SetPadding(8, 0, tmp.right + nMinWidth + 8, 0);
    m_data.objCaption = objCaption;

    nMinWidth = nMinWidth + rcCaption.left + m_data.size.right - rcCaption.right;

    if ((m_data.dwUIStyle & wws_hasicon) == wws_hasicon)
    {
        nMinWidth = nMinWidth + 16 * g_drawing_default_dpi;
        if ((m_data.dwUIStyle & EWS_MESSAGEBOX) == EWS_MESSAGEBOX)
        {
            m_data.lParam = (size_t)m_data.lpMsgParams->hWnd;
        }
    }
    m_data.MinTrackSize.x = nMinWidth;
    m_data.MinTrackSize.y = nMinHeight;
  
}
void JOUI::UIWnd::wm_initmenupopup(HMENU hMenu)
{
    BOOL fChecked = TRUE;
    INT nCount = GetMenuItemCount(hMenu);
    UIFont *hFont = m_data.hFont_Menu;
    if (hFont == 0)
        hFont = g_default_font;
    WCHAR buff[520];
    FLOAT width, height, nMax = 0;
    for (INT i = 0; i < nCount; i++)
    {
        GetMenuStringW(hMenu, i, buff, 520, MF_BYPOSITION);
        m_data.canvas_bkg->CalcTextSize(hFont, buff, -1, DT_SINGLELINE, 0, 0, &width, &height);
        if (nMax < width)
        {
            nMax = width;
        }
    }
    MENUITEMINFOW mii{ 0 };
    mii.cbSize = sizeof(MENUITEMINFOW);
    mii.fMask = MIIM_FTYPE | MIIM_ID;
    for (INT i = 0; i < nCount; i++)
    {
        if (GetMenuItemInfoW(hMenu, i, TRUE, &mii))
        {
            if ((mii.fType & MFT_SEPARATOR) != 0)
            {
                mii.wID = 0;
            }
            mii.fType = mii.fType | MFT_OWNERDRAW;
            if (SetMenuItemInfoW(hMenu, i, TRUE, &mii))
            {
                continue;
            }
        }
        fChecked = FALSE;
        break;
    }
    if (fChecked)
    {
        MENUINFO mi{};
        mi.cbSize = sizeof(MENUINFO);
        mi.fMask = MIM_MENUDATA | MIM_HELPID;
        mi.dwMenuData = (size_t)m_UIWindow;
        mi.dwContextHelpID = nMax;
        if (SetMenuInfo(hMenu, &mi))
        {
            m_data.hMenuPopup = hMenu;
            m_data.dwFlags |= EWF_BMENUINITED;
        }

    }
}
void JOUI::UIWnd::wm_menu_setpos(HWND hWnd, tagWINDOWPOS* pos)
{
    auto pMenuHostWnd = m_data.pMenuHostWnd;
    UIWnd* pMenuPrevWnd = nullptr;
    RECT rcParent = { 0 }, rcDesk = { 0 };
    if (pMenuHostWnd != 0)
    {
        pMenuPrevWnd = pMenuHostWnd->m_data.pMenuPrevWnd;
      
        if (pMenuPrevWnd != 0)
        {
            GetWindowRect(pMenuPrevWnd->m_data.hWnd, &rcParent);
        }
    }
    INT x = pos->x;
    INT y = pos->y;
    POINT pt;
    GetCursorPos(&pt);
    if (rcParent.left < x) //子菜单在右边
    {
        x = rcParent.right + 5;
    }
    else
    {
        //子菜单在左边
        x = rcParent.left;
        GetWindowRect(hWnd, &rcParent);
        x = x - (rcParent.right - rcParent.left) + GetSystemMetrics(SM_CXFIXEDFRAME) * 2 + 1;
        x = x - 5;
    }
    pos->x = x;

    if (pMenuPrevWnd != 0)
    {
        auto pObj = pMenuPrevWnd->m_data.objFocus;
        if (pObj)
        {
            y = pObj->m_data.Frame_w.top + pMenuPrevWnd->m_data.size.top - 4;
            GetWindowRect(hWnd, &rcParent);
            INT height = rcParent.bottom - rcParent.top - GetSystemMetrics(SM_CYFIXEDFRAME) * 2;
            wnd_getscreenrect(hWnd, rcParent, rcDesk);

            if (y + height > rcParent.bottom)
            {
                if (pObj->m_data.Frame_w.bottom < height)
                {
                    y = pObj->m_data.Frame_w.top + pMenuPrevWnd->m_data.size.top - 2 - height + 5;
                }
                else
                {
                    y = pObj->m_data.Frame_w.bottom - height + 2;
                }
            }
        }
    }
    pos->y = y;
}
void JOUI::UIWnd::wm_menu_createitems(HWND hWnd)
{
    size_t hMenu = SendMessageW(hWnd, MN_GETHMENU, 0, 0);
    RECT lpPaddingText{ 32,0,20,0 };
    INT nCount = GetMenuItemCount((HMENU)hMenu);
    auto pMenuHostWnd = m_data.pMenuHostWnd;

    if (pMenuHostWnd && hMenu)
    {
        menu_s* menu = m_data.lpMenuParams;
        RECT rcParent{ 0 };
        GetWindowRect(pMenuHostWnd->m_data.hWnd, &rcParent);
        RECT rcPaddingClient = { (LONG)(5 * g_drawing_default_dpi),(LONG)(2 * g_drawing_default_dpi),(LONG)(5 * g_drawing_default_dpi),(LONG)(2 * g_drawing_default_dpi) };
        INT width = m_data.size.right - (rcPaddingClient.left + rcPaddingClient.right);
        INT height = m_data.size.bottom - (rcPaddingClient.top + rcPaddingClient.bottom);
        auto objTmp = new UIPage(this, rcPaddingClient.left, rcPaddingClient.top, width, height, NULL, EMIS_SUBMENU, eos_ex_focusable);

        Layout_Init(elt_absolute);
        Layout_Absolute_Setedge(objTmp, elcp_absolute_left, elcp_absolute_type_px, rcPaddingClient.left);
        Layout_Absolute_Setedge(objTmp, elcp_absolute_top, elcp_absolute_type_px, rcPaddingClient.top);
        Layout_Absolute_Setedge(objTmp, elcp_absolute_right, elcp_absolute_type_px, rcPaddingClient.right);
        Layout_Absolute_Setedge(objTmp, elcp_absolute_bottom, elcp_absolute_type_px, rcPaddingClient.bottom);

        auto objPP = new UIPage(objTmp, 0, 0, width, height, NULL, 0, eos_ex_focusable, 0);
        objTmp->Layout_Init(elt_absolute);
        objTmp->Layout_Absolute_Setedge(objPP, elcp_absolute_left, elcp_absolute_type_px, 0);
        objTmp->Layout_Absolute_Setedge(objPP, elcp_absolute_top, elcp_absolute_type_px, 0);
        objTmp->Layout_Absolute_Setedge(objPP, elcp_absolute_right, elcp_absolute_type_px, 0);
        objTmp->Layout_Absolute_Setedge(objPP, elcp_absolute_bottom, elcp_absolute_type_px, 0);
        INT heightParent = height;
        height = 0;
        MENUITEMINFOW mii{ 0 };
        mii.cbSize = sizeof(MENUITEMINFOW);
        mii.fMask = MIIM_FTYPE | MIIM_SUBMENU | MIIM_ID;
        RECT rcItem{ 0 };
        INT eos = eos_nodpiscale | eos_scroll_disableno;
        INT offsetTop = 0;
    
        for (INT i = 0; i < nCount; i++)
        {
            if (GetMenuItemRect(pMenuHostWnd->m_data.hWnd, (HMENU)hMenu, i, &rcItem))
            {
                if (GetMenuItemInfoW((HMENU)hMenu, i, TRUE, &mii))
                {
                    if ((mii.fType & MFT_SEPARATOR) != 0) //分隔符
                    {
                        eos = eos | EMIS_SEPARATOR;
                    }
                    else
                    {
                        if (mii.hSubMenu != 0)
                        {

                            eos = eos | EMIS_SUBMENU;
                        }
                    }
                }
                WCHAR buff[520];
                OffsetRect(&rcItem, -rcParent.left, -rcParent.top);
                // 组件超出屏幕左边会出现菜单项目左边负数
                if (rcItem.left < 0)
                {
                    INT offset = abs(rcItem.left);
                    rcItem.left = rcItem.left + offset;
                    rcItem.right = rcItem.right + offset;
                }
                else if (rcItem.left > 0)
                {
                    INT offset = abs(rcItem.left);
                    rcItem.left = rcItem.left - offset;
                    rcItem.right = rcItem.right - offset;
                }
                // 判断第一项，取第一项顶边偏移,组件移到屏幕最顶端二级子菜单第一项会负数
                if (rcItem.top < 0 && i == 0)
                {
                    offsetTop = abs(rcItem.top);
                }
                rcItem.top = rcItem.top + offsetTop;
                rcItem.bottom = rcItem.bottom + offsetTop;
                GetMenuStringW((HMENU)hMenu, i, buff, 520, MF_BYPOSITION);

                auto objitem = new UIItem(objPP, rcItem.left, rcItem.top, width, rcItem.bottom - rcItem.top, buff, eos, eos_ex_focusable,
                    mii.wID, Prefix | SingleLine | Middle);
                objitem->m_data.lParam = i;
                if (m_data.radius)
                    objitem->m_data.radius.left = 6;
                objitem->m_data.dwFlags |= EOF_BMENUITEM;
                if (menu->crText.empty())
                    menu->crText = UIColor(0, 0, 0, 230);
                objitem->SetColor(color_text_normal, menu->crText);
                objitem->SetColor(color_text_hover, menu->crTextHot);
                objitem->SetPadding(32, 0, 20, 0);
                height = height + rcItem.bottom - rcItem.top;
            }
        }

        objPP->SetPos(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, height, 0, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_EX_NODPISCALE);

        height = height - 1;
        //objTmp->SetScrollShow(FALSE, height - heightParent > 0);
        //objTmp->SetScrollInfo(FALSE, SIF_RANGE | SIF_PAGE, 0, height - heightParent, heightParent, 0, TRUE);


        SendMessageW(hWnd, WM_INITMENUPOPUP, hMenu, height);
    }
}
BOOL JOUI::UIWnd::wm_measureitem_host(WPARAM wParam, LPARAM lParam)
{
    BOOL ret = FALSE;
    if (wParam == 0) //menu MEASUREITEMSTRUCT https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-measureitemstruct
    {
        if (((MEASUREITEMSTRUCT*)lParam)->CtlType == ODT_MENU)
        {
            INT nID = ((MEASUREITEMSTRUCT*)lParam)->itemID;
            RECT ppc{ 0,2,0,2 };
            INT offset = ppc.left + ppc.right * g_drawing_default_dpi;

            RECT ppt{ 42,0,20,0 };
            offset += ppt.left + ppt.right * g_drawing_default_dpi;

            INT width, height;
            if (nID == 0) //MF_SEPARATOR
            {
                width = 5 + offset;
                height = 5;//分割线高度
            }
            else
            {
                MENUINFO mi{};
                mi.cbSize = sizeof(MENUINFO);
                mi.fMask = MIM_HELPID;
                GetMenuInfo(m_data.hMenuPopup, &mi);
                width = mi.dwContextHelpID + offset;
                height = 35;//普通项目高度

            }
            ((MEASUREITEMSTRUCT*)lParam)->itemWidth = width;
            ((MEASUREITEMSTRUCT*)lParam)->itemHeight = height;
            ret = TRUE;
        }
    }
    return ret;
}
BOOL JOUI::UIWnd::wm_menu_mouse(HWND hWnd, INT uMsg, WPARAM wParam, LONG_PTR* iItem)
{
    *iItem = -1;
    POINT pt;
    GetCursorPos(&pt);

    wm_nchittest(MAKELONG(pt.x, pt.y));
    wm_mouse(uMsg, wParam, MAKELONG(pt.x, pt.y));
    auto objHittest = m_data.objHittest;
    BOOL ret = FALSE;
    if (objHittest)
    {

        if (((objHittest->m_data.dwStyleEx & eos_ex_focusable) == eos_ex_focusable))
        {

            if (((objHittest->m_data.dwFlags & EOF_BMENUITEM) == EOF_BMENUITEM))
            {
                *iItem = objHittest->m_data.lParam;
            }
            ret = TRUE;
        }
    }

    auto pHost = m_data.pMenuHostWnd;
    if (pHost)
    {
        m_data.pMenuTrackWnd = (UIWnd*)m_UIWindow;
    }
    return ret;
}
DWORD JOUI::UIWnd::UIStyle(DWORD wwsStyle, BOOL dwNewlong)
{
    if (wwsStyle == 0)
        return m_data.dwUIStyle;
    if (dwNewlong)
    {
        m_data.dwUIStyle = wwsStyle;
    }
    else
    {
        if (FLAGS_CHECK(m_data.dwUIStyle, wwsStyle))
        {
            FLAGS_DEL(m_data.dwUIStyle, wwsStyle);
        }
        else
        {
            FLAGS_ADD(m_data.dwUIStyle, wwsStyle);
        }
    }
    FLAGS_DEL(m_data.dwFlags, EWF_BRENDERED); //强制重画
    InvalidateRect(m_data.hWnd, NULL, FALSE);
    UpdateWindow(m_data.hWnd);
    return 0;
}
INT JOUI::UIWnd::Style(INT dwStyle)
{
    if (dwStyle == 0)
        return GetWindowLongPtrW(m_data.hWnd, GWL_STYLE);
    if (_wnd_querystyle(m_data.hWnd, dwStyle, FALSE))
    {
        _wnd_delstyle(m_data.hWnd, dwStyle, FALSE);
    }
    else
    {
        _wnd_addstyle(m_data.hWnd, dwStyle, FALSE);
    }
    return 0;
}
INT JOUI::UIWnd::StyleEx(INT dwStyleEx)
{
    if (dwStyleEx == 0)
        return GetWindowLongPtrW(m_data.hWnd, GWL_EXSTYLE);
    if (_wnd_querystyle(m_data.hWnd, dwStyleEx, TRUE))
    {
        _wnd_delstyle(m_data.hWnd, dwStyleEx, TRUE);
    }
    else
    {
        _wnd_addstyle(m_data.hWnd, dwStyleEx, TRUE);
    }
    return 0;
}
BOOL JOUI::UIWnd::Move(INT x, INT y, INT width, INT height, BOOL bRepaint)
{
    RECT rect = { 0 };
    GetWindowRect(m_data.hWnd, &rect);
    if (x == CW_USEDEFAULT)
        x = rect.left;
    if (y == CW_USEDEFAULT)
        y = rect.top;
    if (width == CW_USEDEFAULT)
        width = rect.right - rect.left;
    if (height == CW_USEDEFAULT)
        height = rect.bottom - rect.top;
    return ::MoveWindow(m_data.hWnd, x, y, width, height, bRepaint);
}
void JOUI::UIWnd::Update(BOOL bUpdate)
{
    m_data.dwFlags |= EWF_BREDRAWBACKGROUND;
    InvalidateRect(m_data.hWnd, NULL, FALSE);

    if (bUpdate)
    {
        auto atomName = GetClassLongPtrW(m_data.hWnd, GCW_ATOM);
        if (atomName == ATOM_DIALOG) //判断是模态窗口,添加模态窗口风格
        {
            SetFocus(m_data.hWnd);
        }
        Layout_Update();
        UpdateWindow(m_data.hWnd);
    }
}

void JOUI::UIWnd::SetShadowColor(UIColor dwColor, INT fSize, BOOL fRedraw)
{
    if (m_data.crshadow)
        delete m_data.crshadow;
    m_data.crshadow = nullptr;
    if (!dwColor.empty())
        m_data.crshadow = new UIBrush(dwColor);
    if (fSize)
        m_data.size_shadow = fSize;
    if (fRedraw)
    {
        FLAGS_DEL(m_data.dwFlags, EWF_BRENDERED);
        InvalidateRect(m_data.hWnd, NULL, FALSE);
    }

}
void JOUI::UIWnd::SetBorderColor(UIColor dwColor, INT fSize, BOOL fRedraw)
{
    if (m_data.crBorder)
        delete m_data.crBorder;
    m_data.crBorder = nullptr;
    if (!dwColor.empty())
        m_data.crBorder = new UIBrush(dwColor);
    if (fSize)
        m_data.size_Border = fSize;
    if (fRedraw)
    {
        FLAGS_DEL(m_data.dwFlags, EWF_BRENDERED);
        InvalidateRect(m_data.hWnd, NULL, FALSE);
    }
}

void JOUI::UIWnd::SetBackgColor(UIColor dwColor, BOOL fRedraw)
{
    if (m_data.crbkg)
        delete m_data.crbkg;
    m_data.crbkg = nullptr;
    if (!dwColor.empty())
    {
        m_data.crbkg = new UIBrush(dwColor);
        m_data.dwUIStyle |= wws_noinheritbkg;
    }
    if (fRedraw)
    {
        m_data.dwFlags |= EWF_BREDRAWBACKGROUND;
        InvalidateRect(m_data.hWnd, NULL, FALSE);
        UpdateWindow(m_data.hWnd);
    }
}

BOOL JOUI::UIWnd::SetBackgImage(LPVOID lpImage, size_t dwImageLen, INT x, INT y, DWORD dwRepeat, RECT* lpGrid, INT dwFlags, DWORD dwAlpha)
{
    if (dwImageLen != 0)
    {
        auto hImg = new UIImage(lpImage, dwImageLen);
        return wm_setbackgImage(hImg, x, y, dwRepeat, lpGrid, dwFlags, dwAlpha);
    }
    return wm_setbackgImage(0, x, y, dwRepeat, lpGrid, dwFlags, dwAlpha);
}

BOOL JOUI::UIWnd::SetBackgImage(UIImage *hImage, BOOL fReset, INT x, INT y, DWORD dwRepeat, RECT* lpGrid, INT dwFlags, DWORD dwAlpha)
{
    if(fReset)
        return wm_setbackgImage(0, 0, 0, 0, 0, 0, 0);
    return wm_setbackgImage(hImage, x, y, dwRepeat, lpGrid, dwFlags, dwAlpha);
}

BOOL JOUI::UIWnd::SetBackgImage(LPCWSTR lpImagefile, INT x, INT y, DWORD dwRepeat, RECT* lpGrid, INT dwFlags, DWORD dwAlpha)
{
    if (lpImagefile)
    {
        auto hImg = new UIImage(lpImagefile);
        return wm_setbackgImage(hImg, x, y, dwRepeat, lpGrid, dwFlags, dwAlpha);
    }
    return wm_setbackgImage(0, x, y, dwRepeat, lpGrid, dwFlags, dwAlpha);
}
void JOUI::UIWnd::SetBackgInfo(INT x, INT y, DWORD dwRepeat, RECT* lpGrid, INT dwFlags, DWORD dwAlpha)
{
    auto lpBI = m_data.lpBackgroundImage;
    if (lpBI)
    {
        lpBI->dwFlags = dwFlags;
        lpBI->x = x;
        lpBI->y = y;
        lpBI->dwRepeat = dwRepeat;
        lpBI->dwAlpha = dwAlpha;
        lpBI->lpGrid = lpGrid;
    }
}
BOOL JOUI::UIWnd::SetBackgPlay(BOOL fPlayFrames, BOOL fResetFrame)
{
    auto lpBI = m_data.lpBackgroundImage;
    if (lpBI)
    {
        auto pImg = lpBI->hImage;
        if (pImg->GetFrameCount() > 1)
        {
            if (fPlayFrames && lpBI->nDelay != 0)
            {
                lpBI->dwFlags = lpBI->dwFlags | bif_playimage;
                ::SetTimer(m_data.hWnd, ((size_t)this + TIMER_BKG), lpBI->nDelay * 10, wm_backimage_timer);
            }
            else
            {
                lpBI->dwFlags = lpBI->dwFlags - (lpBI->dwFlags & bif_playimage);
                ::KillTimer(m_data.hWnd, (UINT_PTR)((size_t)this + TIMER_BKG));
            }
            if (fResetFrame)
                pImg->SetCurFrame(0);

            return TRUE;
        }
    }
    return FALSE;
}

void JOUI::UIWnd::SetIme(BOOL bEnable)
{
    ImmAssociateContext(m_data.hWnd, (bEnable ? m_data.hImc : 0));
}

void JOUI::UIWnd::SetRadius(INT radius)
{
    m_data.radius = radius;
    m_data.radius_r = radius;
    auto objCaption = (UICombutton*)m_data.objCaption;
    if (objCaption)
        objCaption->SetRadius(radius + 5, radius + 5, 0, 0);

    m_data.dwFlags |= EWF_BREDRAWBACKGROUND;
    InvalidateRect(m_data.hWnd, NULL, FALSE);
}
INT JOUI::UIWnd::GetRadius()
{
    return m_data.radius;
}
void JOUI::UIWnd::SetAlpha(INT fAlpha)
{
    m_data.alpha = fAlpha;
}
void JOUI::UIWnd::SetCaptionInfo(info_objcaption *Info)
{
    if (m_data.objCaption && Info)
    {
        auto objCaption = (UICombutton*)m_data.objCaption;
        if (Info->hicon) {
            objCaption->SetIcon(Info->hicon);
        }
        auto sObj = (UICombutton*)objCaption->m_objChildFirst;
        while (sObj)
        {
            sObj->SetCrbutton(Info->crbutton_normal, Info->crbutton_hover, Info->crbutton_down);
            sObj->SetCrBkgbutton(Info->crBkgbutton_normal, Info->crBkgbutton_hover, Info->crBkgbutton_down);
            if (Info->Bkgbutton_radius != 0)
                sObj->SetRadiusbutton(Info->Bkgbutton_radius);
            switch (sObj->GetID())
            {
            case wws_button_close:
                sObj->SetImgbutton(Info->imgbutton_close);
                break;
            case wws_button_max:
                sObj->SetImgbutton(Info->imgbutton_max, Info->imgbutton_restore);
                break;
            case wws_button_min:
                sObj->SetImgbutton(Info->imgbutton_min);
                break;
            case wws_button_menu:
                sObj->SetImgbutton(Info->imgbutton_menu);
                break;
            case wws_button_skin:
                sObj->SetImgbutton(Info->imgbutton_skin);
                break;
            case wws_button_setting:
                sObj->SetImgbutton(Info->imgbutton_setting);
                break;
            case wws_button_help:
                sObj->SetImgbutton(Info->imgbutton_help);
                break;
            default:
                return;
            }
            sObj = (UICombutton*)sObj->GetNode(GW_HWNDNEXT);
        }
       
        objCaption->m_pfnSubClass = Info->pfnSubmsg;
        if (Info->Fontinfo)
        {
            objCaption->SetFontLogFont(Info->Fontinfo, TRUE);
        }
        else if (Info->lpszFontfamily != NULL || Info->dwFontsize != 0 || Info->dwFontstyle != 0)
        {
            objCaption->SetFontFromFamily(Info->lpszFontfamily, Info->dwFontsize, Info->dwFontstyle, TRUE);
        }
        if (!Info->crTitle.empty())
            objCaption->SetColor(color_text_normal, Info->crTitle);
        if (Info->crBkg.GetR() != 0.0f || Info->crBkg.GetG() != 0.0f || Info->crBkg.GetB() != 0.0f)
            objCaption->SetColor(color_background, Info->crBkg);
        if (Info->titlebHeight != 0)
            m_data.size_caption.cy = Info->titlebHeight;
        if (Info->dwTextFormat != 0)
            objCaption->SetTextFormat(Info->dwTextFormat);
        objCaption->Redraw();
    }
}
LPVOID JOUI::UIWnd::GetCaptionInfo(info_objcaption& Info, INT uType)
{
    if (m_data.objCaption)
    {
        auto objCaption = (UICombutton*)m_data.objCaption;
        auto sObj = (UICombutton*)objCaption->m_objChildFirst;

        switch (uType)
        {
        case 1:
            sObj->GetCrbutton(Info.crbutton_normal, Info.crbutton_hover, Info.crbutton_down);
            break;
        case 2:
            sObj->GetCrBkgbutton(Info.crBkgbutton_normal, Info.crBkgbutton_hover, Info.crBkgbutton_down);
            break;
        default:break;
        }
    }
    return m_data.objCaption;
}
void JOUI::UIWnd::SetBlur(FLOAT fDeviation, BOOL fmodesoft, BOOL bRedraw)
{
    m_data.fBlur = fDeviation;
    m_data.fmodesoft = fmodesoft;
    if (bRedraw)
        InvalidateRect(m_data.hWnd, 0, 0);
}
INT JOUI::UIWnd::PopupMsg(LPCWSTR lpText, LPCWSTR lpTitle, INT uType, LPCWSTR lpCheckBox, BOOL* lpCheckBoxChecked, INT dwUIStyle, UIColor crText, UIColor crBackground,
    LPVOID dwButfig, INT dwMilliseconds, WinMsgPROC lpfnMsgProc)
{
    mbp_s mbp{};
    mbp.pWnd = m_UIWindow;
    mbp.lpText = lpText;
    mbp.lpTitle = lpTitle;
    mbp.uType = uType;
    mbp.lpCheckBox = lpCheckBox;
    mbp.lpCheckBoxChecked = lpCheckBoxChecked;
    mbp.dwFlags = dwUIStyle;
    mbp.lpfnNotifyCallback = lpfnMsgProc;
    mbp.crText = crText;
    mbp.crBackground = crBackground;
    mbp.hWnd = m_data.hWnd;
    mbp.radius = m_data.radius;
    if (m_data.crshadow)
        m_data.crshadow->GetColor(mbp.crshadow);
    mbp.dwButfig = dwButfig;
    m_data.lpMsgParams = &mbp;
    if (dwMilliseconds)
        return MessageBoxTimeoutW(m_data.hWnd, lpText, lpTitle, uType, 0, dwMilliseconds);
    else
        return MessageBoxW(m_data.hWnd, lpText, lpTitle, uType);
}
BOOL JOUI::UIWnd::PopupTrayIcon(LPCWSTR lpwzInfo, LPCWSTR lpwzInfoTitle, INT dwInfoFlags)
{
    BOOL nError = 0;
    LPVOID lpNid = m_data.lpNid;
    if (lpNid != 0)
    {
        ((NOTIFYICONDATAW*)lpNid)->uFlags = NIF_INFO;
        RtlZeroMemory((LPVOID)((size_t)lpNid + offsetof(NOTIFYICONDATAW, szInfo)), 512);
        INT len = lstrlenW(lpwzInfo);
        if (len > 255)
            len = 255;
        RtlMoveMemory((LPVOID)((size_t)lpNid + offsetof(NOTIFYICONDATAW, szInfo)), lpwzInfo, len * 2);
        RtlZeroMemory((LPVOID)((size_t)lpNid + offsetof(NOTIFYICONDATAW, szInfoTitle)), 128);
        len = lstrlenW(lpwzInfoTitle);
        if (len > 63)
            len = 63;
        if (len)
            RtlMoveMemory((LPVOID)((size_t)lpNid + offsetof(NOTIFYICONDATAW, szInfoTitle)), lpwzInfoTitle, len * 2);
        ((NOTIFYICONDATAW*)lpNid)->dwInfoFlags = dwInfoFlags;
        nError = Shell_NotifyIconW(NIM_MODIFY, (PNOTIFYICONDATAW)lpNid);
    }
    return nError;
}

HRESULT JOUI::UIWnd::ToImage(UIImage**dstImg, BOOL cdraw)
{
    if (!m_data.canvas_display)
        return E_FAIL;
    return m_data.canvas_display->ToImage(dstImg, cdraw);
}

UINT JOUI::UIWnd::GetLastFPS() const
{
    return m_data.fpsCache;
}
void JOUI::UIWnd::SetRenderMode(bool active)
{
    m_data.renderMode = active;
}